toml-parser
TOML 1.0.0 parser
Version on this page: | 1.3.2.0 |
LTS Haskell 23.4: | 2.0.1.0@rev:1 |
Stackage Nightly 2025-01-15: | 2.0.1.0@rev:1 |
Latest on Hackage: | 2.0.1.0@rev:1 |
toml-parser-1.3.2.0@sha256:50adac7c438c0217e5968646618d5316c3c74f9c14e036976999f9b90a612306,3625
Module documentation for 1.3.2.0
TOML Parser
This package implements a validating parser for TOML 1.0.0.
This package uses an alex-generated lexer and happy-generated parser.
It also provides a pair of classes for serializing into and out of TOML.
Package Structure
---
title: Package Structure
---
stateDiagram-v2
classDef important font-weight:bold;
TOML:::important --> ApplicationTypes:::important : decode
ApplicationTypes --> TOML : encode
TOML --> [Token]: Toml.Lexer
[Token] --> [Expr]: Toml.Parser
[Expr] --> Table : Toml.Semantics
Table --> ApplicationTypes : Toml.FromValue
ApplicationTypes --> Table : Toml.ToValue
Table --> TOML : Toml.Pretty
The highest-level interface to this package is to define FromValue
and ToTable
instances for your application-specific datatypes. These can be used with encode
and decode
to convert to and from TOML.
For low-level access to the TOML format, the lexer, parser, and validator are available for direct use. The diagram above shows how the different modules enable you to advance through the increasingly high-level TOML representations.
Examples
This file uses markdown-unlit to ensure that its code typechecks and stays in sync with the rest of the package.
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.Generic (GenericTomlTable(..))
import Toml.ToValue (ToValue(toValue), ToTable(toTable), defaultTableToValue, table, (.=))
main :: IO ()
main = hspec (parses >> decodes >> encodes)
Using the raw parser
Consider this sample TOML text from the TOML specification.
fruitStr :: String
fruitStr = [quoteStr|
[[fruits]]
name = "apple"
[fruits.physical] # subtable
color = "red"
shape = "round"
[[fruits.varieties]] # nested array of tables
name = "red delicious"
[[fruits.varieties]]
name = "granny smith"
[[fruits]]
name = "banana"
[[fruits.varieties]]
name = "plantain"
|]
Parsing using this package generates the following value
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.
newtype Fruits = Fruits { fruits :: [Fruit] }
deriving (Eq, Show, Generic)
deriving (ToTable, ToValue, FromValue) via GenericTomlTable Fruits
data Fruit = Fruit { name :: String, physical :: Maybe Physical, varieties :: [Variety] }
deriving (Eq, Show, Generic)
deriving (ToTable, ToValue, FromValue) via GenericTomlTable Fruit
data Physical = Physical { color :: String, shape :: String }
deriving (Eq, Show)
newtype Variety = Variety String
deriving (Eq, Show)
instance FromValue Physical where
fromValue = parseTableFromValue (Physical <$> reqKey "color" <*> reqKey "shape")
instance FromValue Variety where
fromValue = parseTableFromValue (Variety <$> reqKey "name")
We can run this example on the original value to deserialize it into domain-specific datatypes.
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 eligible to be top-level types (all TOML documents
are tables at the top-level).
Generics can be used to derive ToTable
for simple record types.
Manually defined instances are available for the more complex cases.
instance ToValue Physical where toValue = defaultTableToValue
instance ToTable Physical where toTable x = table ["color" .= color x, "shape" .= shape x]
instance ToValue Variety where toValue = defaultTableToValue
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"
[[fruits.varieties]]
name = "red delicious"
[[fruits.varieties]]
name = "granny smith"|]
More Examples
A demonstration of using this package at a more realistic scale can be found in HieDemoSpec. 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.
Changes
Revision history for toml-parser
1.3.2.0
- Added
Toml.Generic
to make instances easily derivable via DerivingVia. - Added GHC.Generics support for switching between product types and TOML arrays.
1.3.1.3
- Bugfix: Previous fix admitted some invalid inline tables - these are now rejected
1.3.1.2
- Bugfix: In some cases overlapping keys in inline tables could throw an exception instead instead of returning the proper semantic error value.
1.3.1.1
- Ensure years are rendered zero-padded
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.
Consumers of the
Toml
module can keep getting simple error strings and users interested in structured errors can run the different layers independently to get more detailed error reporting. FromValue
andToValue
instances for:Ratio
,NonEmpty
,Seq
- Add
FromKey
andToKey
for allowing codecs forMap
to use various key types.
1.2.1.0 – 2023-07-12
- Added
Toml.Pretty.prettyTomlOrdered
to allow user-specified section ordering. - Added
FromValue
andToValue
instances forText
- Added
reqKeyOf
andoptKeyOf
for easier custom matching withoutFromValue
instances.
1.2.0.0 – 2023-07-09
-
Remove
FromTable
class. This class existed for things that could be matched specifically from tables, which is what the top-level values always are. HoweverFromValue
already handles this, and both classes can fail, so having the extra level of checking doesn’t avoid failure. It does, however, create a lot of noise generating instances. Note thatToTable
continues to exist becausetoTable
isn’t allowed to fail, and when serializing to TOML syntax you can only serialize top-level tables. -
Extracted
Toml.FromValue.Matcher
andToml.FromValue.ParseTable
into their own modules. -
Add
pickKey
,liftMatcher
,inKey
,inIndex
,parseTableFromValue
toToml.FromValue
-
Replace
genericFromTable
withgenericParseTable
. The intended way to derive aFromValue
instance is now to write:instance FromValue T where fromValue = parseTableFromValue genericParseTable
1.1.1.0 – 2023-07-03
- Add support for GHC 8.10.7 and 9.0.2
1.1.0.0 – 2023-07-03
- Add Toml.FromValue.Generic and Toml.ToValue.Generic
- Add Alternative instance to Matcher and support multiple error messages in Result
- Add Data and Generic instances for Value
1.0.1.0 – 2023-07-01
- Add ToTable and ToValue instances for Map
- Refine error messages
- More test coverage
1.0.0.0 – 2023-06-29
- Complete rewrite including 1.0.0 compliance and pretty-printing.
0.1.0.0 – 2017-05-04
- First version.