content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE Rank2Types #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE GADTs #-}
  ----------------------------------------------------------------------------
  -- |
  -- Module      :  Codec.Winery.Query.Parser
  -- Copyright   :  (c) Fumiaki Kinoshita 2019
  -- License     :  BSD3
  -- Stability   :  Experimental
  --
  -- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
  --
  -- The language for winery queries
  --
  -- See the Pretty-printing section of README.md for examples.
  --
  -----------------------------------------------------------------------------
  module Codec.Winery.Query.Parser (parseQuery) where
  
  import Prelude hiding ((.), id)
  import Control.Category
  import Codec.Winery.Query
  import Text.Megaparsec
  import Text.Megaparsec.Char
  import qualified Text.Megaparsec.Char.Lexer as L
  import qualified Data.Text as T
  import Prettyprinter (Doc, hsep)
  import Data.Typeable
  import Data.Void
  
  type Parser = Parsec Void T.Text
  
  symbol :: T.Text -> Parser T.Text
  symbol = L.symbol space
  
  name :: Parser T.Text
  name = fmap T.pack (some (alphaNumChar <|> oneOf ("_\'" :: [Char])) <?> "field name")
  
  parseQuery :: Typeable a => Parser (Query (Doc a) (Doc a))
  parseQuery = foldr (.) id <$> sepBy1 parseTerms (symbol "|")
  
  -- | Space-separated list of terms translate to a tabular output, applying the
  -- queries in parallel
  parseTerms :: Typeable a => Parser (Query (Doc a) (Doc a))
  parseTerms = fmap hsep . sequenceA <$> sepBy1 parseTerm space
  
  parseTerm :: Typeable a => Parser (Query (Doc a) (Doc a))
  parseTerm = L.lexeme space $ choice
    [ char '.' >> choice
      [ do
        _ <- char '['
        i <- optional L.decimal
        j <- optional (symbol ":" >> L.decimal)
        _ <- char ']'
        return $ range (maybe 0 id i) (maybe (-1) id j)
      , productItem <$> L.decimal
      , field <$> name
      , return id
      ]
    ]
  