module Toml.Parser.TOML
( keyP
, hasKeyP
, tableP
, tableArrayP
, inlineTableP
, tomlP
) where
import Control.Applicative (Alternative (..))
import Control.Monad.Combinators (between, eitherP, optional, sepEndBy)
import Data.List.NonEmpty (NonEmpty (..), (<|))
import Data.Semigroup ((<>))
import Data.Text (Text)
import Toml.Parser.Core (Parser, alphaNumChar, char, lexeme, sc, text, try)
import Toml.Parser.String (basicStringP, literalStringP)
import Toml.Parser.Value (anyValueP)
import Toml.PrefixTree (Key (..), KeysDiff (..), Piece (..), fromList, keysDiff)
import Toml.Type (AnyValue, TOML (..))
import qualified Control.Applicative.Combinators.NonEmpty as NC
import qualified Data.HashMap.Lazy as HashMap
import qualified Data.Text as Text
bareKeyPieceP :: Parser Text
bareKeyPieceP = lexeme $ Text.pack <$> bareStrP
where
bareStrP :: Parser String
bareStrP = some $ alphaNumChar <|> char '_' <|> char '-'
keyComponentP :: Parser Piece
keyComponentP = Piece <$>
(bareKeyPieceP <|> (quote "\"" <$> basicStringP) <|> (quote "'" <$> literalStringP))
where
quote :: Text -> Text -> Text
quote q t = q <> t <> q
keyP :: Parser Key
keyP = Key <$> NC.sepBy1 keyComponentP (char '.')
tableNameP :: Parser Key
tableNameP = between (text "[") (text "]") keyP
tableArrayNameP :: Parser Key
tableArrayNameP = between (text "[[") (text "]]") keyP
hasKeyP :: Parser (Key, Either AnyValue TOML)
hasKeyP = (,) <$> keyP <* text "=" <*> eitherP anyValueP inlineTableP
inlineTableP :: Parser TOML
inlineTableP = between
(text "{") (text "}")
(tomlFromInline <$> hasKeyP `sepEndBy` text ",")
tableP :: Parser (Key, TOML)
tableP = do
key <- tableNameP
toml <- subTableContent key
pure (key, toml)
tableArrayP :: Parser (Key, NonEmpty TOML)
tableArrayP = do
key <- tableArrayNameP
localToml <- subTableContent key
more <- optional $ sameKeyP key tableArrayP
case more of
Nothing -> pure (key, localToml :| [])
Just (_, tomls) -> pure (key, localToml <| tomls)
tomlP :: Parser TOML
tomlP = do
sc
(val, inline) <- distributeEithers <$> many hasKeyP
(table, array) <- fmap distributeEithers
$ many
$ eitherPairP (try tableP) tableArrayP
pure TOML
{ tomlPairs = HashMap.fromList val
, tomlTables = fromList $ inline ++ table
, tomlTableArrays = HashMap.fromList array
}
subTableContent :: Key -> Parser TOML
subTableContent key = do
(val, inline) <- distributeEithers <$> many hasKeyP
(table, array) <- fmap distributeEithers
$ many
$ childKeyP key
$ eitherPairP (try tableP) tableArrayP
pure TOML
{ tomlPairs = HashMap.fromList val
, tomlTables = fromList $ inline ++ table
, tomlTableArrays = HashMap.fromList array
}
childKeyP :: Key -> Parser (Key, a) -> Parser (Key, a)
childKeyP key p = try $ do
(k, x) <- p
case keysDiff key k of
FstIsPref k' -> pure (k', x)
_ -> fail $ show k ++ " is not a child key of " ++ show key
sameKeyP :: Key -> Parser (Key, a) -> Parser (Key, a)
sameKeyP key parser = try $ do
(k, x) <- parser
case keysDiff key k of
Equal -> pure (k, x)
_ -> fail $ show k ++ " is not the same as " ++ show key
tomlFromInline :: [(Key, Either AnyValue TOML)] -> TOML
tomlFromInline kvs =
let (lefts, rights) = distributeEithers kvs
in TOML (HashMap.fromList lefts) (fromList rights) mempty
distributeEithers :: [(c, Either a b)] -> ([(c, a)], [(c, b)])
distributeEithers = foldr distribute ([], [])
where
distribute :: (c, Either a b) -> ([(c, a)], [(c, b)]) -> ([(c, a)], [(c, b)])
distribute (k, Left a) (ls, rs) = ((k, a) : ls, rs)
distribute (k, Right b) (ls, rs) = (ls, (k, b) : rs)
eitherPairP :: Alternative m => m (c, a) -> m (c, b) -> m (c, Either a b)
eitherPairP a b = (fmap Left <$> a) <|> (fmap Right <$> b)