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.FromTable
ApplicationTypes --> Table : Toml.ToTable
Table --> TOML : Toml.Pretty
The highest-level interface to this package is to define FromTable
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.
Example
Consider this sample TOML text from the specification.
[[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
>>> Right fruitToml = parse fruitStr
>>> fruitToml
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.
Here's an example of defining datatypes and deserializers for the TOML above.
newtype Fruits = Fruits [Fruit]
deriving (Eq, Show)
data Fruit = Fruit String (Maybe Physical) [Variety]
deriving (Eq, Show)
data Physical = Physical String String
deriving (Eq, Show)
newtype Variety = Variety String
deriving (Eq, Show)
instance FromTable Fruits where
fromTable = runParseTable (Fruits <$> reqKey "fruits")
instance FromTable Fruit where
fromTable = runParseTable (Fruit <$> reqKey "name" <*> optKey "physical" <*> reqKey "varieties")
instance FromTable Physical where
fromTable = runParseTable (Physical <$> reqKey "color" <*> reqKey "shape")
instance FromTable Variety where
fromTable = runParseTable (Variety <$> reqKey "name")
instance FromValue Fruits where fromValue = defaultTableFromValue
instance FromValue Fruit where fromValue = defaultTableFromValue
instance FromValue Physical where fromValue = defaultTableFromValue
instance FromValue Variety where fromValue = defaultTableFromValue
We can run this example on the original value to deserialize it into domain-specific datatypes.
>>> decode fruitToml :: Either String Fruits
Right (Fruits [
Fruit "apple" (Just (Physical "red" "round")) [Variety "red delicious", Variety "granny smith"],
Fruit "banana" Nothing [Variety "plantain"]])