{- |
    Moduel : Data.Text.Interp.Parse

    Parsing types and functions
-}
{-# LANGUAGE OverloadedStrings #-}

module Data.Text.Interp.Parse where

import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
import Data.Text (Text, pack)
import Data.Void (Void)
import Text.Megaparsec
import Text.Megaparsec.Char

import Data.Text.Interp.Types

type Parser = Parsec Void Text

-- | Parse `Data.Text.Interp.Types.IText`s
itP :: Parser (NonEmpty IText)
itP = NE.fromList <$> (some $ try $ toInterpolateP <|> rawTextP)

interpStart = "{{"
interpStop = "}}"

-- | parse raw text (i.e., text without interpolations)
rawTextP :: Parser IText
rawTextP = RawText <$> takeWhile1P (Just "raw text") (\c -> not (c `elem` ['}', '{']))

-- | parse text that needs interpolation
toInterpolateP :: Parser IText
toInterpolateP = between (string interpStart >> space)
                         (space >> string interpStop)
                 interP

-- | parse interpolation instructions (with or without binding)
interP :: Parser IText
interP = try $ bindP <|> keyP

-- | parse interpolation that needs binding
bindP :: Parser IText
bindP = do
  char '('
  bk <- key <* char '#'
  bp <- NE.fromList <$> keys <* string ")"
  path <- (optional (char '.')) *> keys
  let b = Just $ Binding bk bp
  return $ ToInterpolate b path

-- | parse interpolation that doesn't need binding
keyP :: Parser IText
keyP = ToInterpolate Nothing <$> keys

-- | parse a single key
key :: Parser Key
key = Key <$> pack <$> some (alphaNumChar <|> satisfy (`elem` ['_', '-'])) <?>
          "keys can only contain letters, numbers, _, and -"

-- | parse a list of keys
keys :: Parser [Key]
keys = sepBy key (char '.')