content: {-# OPTIONS_GHC -Wno-unused-do-bind #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Parsers where
  import           Common               (Focuser (..), Mapping, Parser,
                                         Range (..), composeFocusers, focusTo,
                                         foldFocusers, foldMappings, integer,
                                         lexeme, mappingTo, rational,
                                         showRational, symbol)
  import           Data.Char            (isAlphaNum)
  import           Data.Functor         (($>))
  import           Data.Maybe           (fromMaybe)
  import           Data.Text            (Text)
  import qualified Data.Text            as T
  import           Focusers             (escaping, focusAtIdx, focusAtKey,
                                         focusAverage, focusCollect, focusCols,
                                         focusCompEq, focusCompOrd, focusConst,
                                         focusContains, focusEach, focusEl,
                                         focusEndsWith, focusFilter, focusId,
                                         focusIf, focusIndex, focusIsAlpha,
                                         focusIsAlphaNum, focusIsDigit,
                                         focusIsLower, focusIsNumber,
                                         focusIsSpace, focusIsUpper, focusKV,
                                         focusKey, focusLength, focusLines,
                                         focusLogic2, focusLogicMany, focusMaxBy,
                                         focusMaxLexBy, focusMinBy, focusMinLexBy,
                                         focusNot, focusProduct, focusRegex,
                                         focusSlice, focusSortedBy,
                                         focusSortedLexBy, focusSpace,
                                         focusStartsWith, focusSum, focusVal,
                                         focusWords)
  import           Mappings             (mappingAbs, mappingAdd, mappingAppend,
                                         mappingDiv, mappingId, mappingLength,
                                         mappingLower, mappingMap, mappingMult,
                                         mappingPow, mappingPrepend,
                                         mappingReverse, mappingSign,
                                         mappingSlice, mappingSortBy,
                                         mappingSortLexBy, mappingSub,
                                         mappingUpper)
  import           Text.Megaparsec      (MonadParsec (try), anySingle,
                                         anySingleBut, between, choice, empty,
                                         label, many, noneOf, notFollowedBy,
                                         optional, satisfy, sepBy, sepBy1,
                                         takeWhile1P, (<|>))
  import           Text.Megaparsec.Char (char, string)
  
  -- Focuser parsers
  
  parseFocuser :: Parser Focuser
  parseFocuser = label "valid focuser" $ choice
      [ symbol "id" $> focusId
      , symbol "each" $> focusEach
      , symbol "words" $> focusWords
      , symbol "lines" $> focusLines
      , symbol "ws" $> focusSpace
      , symbol "cols" $> focusCols
      , parseFocusSlice
      , parseFocusSortedLexBy
      , symbol "sortedLex" $> focusSortedLexBy focusId
      , parseFocusMinLexBy
      , parseFocusMaxLexBy
      , symbol "minLex" $> focusMinLexBy focusId
      , symbol "maxLex" $> focusMaxLexBy focusId
      , parseFocusSortedBy
      , parseFocusIndex
      , symbol "sorted" $> focusSortedBy focusId
      , parseFocusTo
      , symbol "len" $> focusLength
      , parseFocusMinBy
      , parseFocusMaxBy
      , symbol "min" $> focusMinBy focusId
      , symbol "max" $> focusMaxBy focusId
      , between (symbol "(") (symbol ")") $ foldFocusers <$> parseFocusers
      , symbol "sum" $> focusSum
      , symbol "product" $> focusProduct
      , parseFocusAverage
      , parseFocusAdd
      , parseFocusSub
      , parseFocusMult
      , parseFocusDiv
      , parseFocusPow
      , symbol "abs" $> focusTo mappingAbs
      , symbol "sign" $> focusTo mappingSign
      , parseFocusIf
      , symbol "isUpper" $> focusIsUpper
      , symbol "isLower" $> focusIsLower
      , symbol "isDigit" $> focusIsDigit
      , symbol "isAlphaNum" $> focusIsAlphaNum
      , symbol "isAlpha" $> focusIsAlpha
      , symbol "isSpace" $> focusIsSpace
      , symbol "isNumber" $> focusIsNumber
      , parseFocusRegex
      , parseFocusFilter
      , parseFocusContains
      , parseFocusStartsWith
      , parseFocusEndsWith
      , symbol "el" $> focusEl
      , symbol "kv" $> focusKV
      , symbol "key" $> focusKey
      , symbol "val" $> focusVal
      , parseFocusAtKey
      , parseFocusAtIdx
      , parseFocusAll
      , parseFocusAny
      , symbol "not" $> focusNot
      , parseFocusOr
      , parseFocusAnd
      , parseFocusEq
      , parseFocusNeq
      , parseFocusLeq
      , parseFocusGeq
      , try parseFocusLt <|> parseFocusCollect
      , parseFocusGt
      , parseFocusLit
      ]
  
  parseFocusers :: Parser [Focuser]
  parseFocusers = label "valid focuser stack" $ parseFocuser `sepBy1` symbol "."
  
  parseFocusCollect :: Parser Focuser
  parseFocusCollect = do
      symbol "%"
      focusCollect <$> parseFocuser
  
  parseFocusSlice :: Parser Focuser
  parseFocusSlice = do
      ranges <- between (symbol "{") (symbol "}") (range `sepBy` symbol ",")
      return $ focusSlice ranges
  
  range :: Parser Range
  range = try rangeRange <|> rangeSingle
  
  rangeSingle :: Parser Range
  rangeSingle = RangeSingle <$> integer
  
  rangeRange :: Parser Range
  rangeRange = label "range" $ do
      mstart <- optional integer
      symbol ":"
      mend <- optional integer
      return $ RangeRange mstart mend
  
  parseFocusSortedBy :: Parser Focuser
  parseFocusSortedBy = do
      lexeme $ string "sortedBy" >> notFollowedBy (satisfy isAlphaNum)
      focusSortedBy <$> parseFocuser
  
  parseFocusIndex :: Parser Focuser
  parseFocusIndex = do
      symbol "["
      n <- integer
      symbol "]"
      return $ focusIndex n
  
  parseFocusTo :: Parser Focuser
  parseFocusTo = do
      lexeme $ string "to" >> notFollowedBy (satisfy isAlphaNum)
      mapping <- foldMappings <$> parseMappings
      return $ focusTo mapping
  
  parseFocusMinBy :: Parser Focuser
  parseFocusMinBy = do
      lexeme $ string "minBy" >> notFollowedBy (satisfy isAlphaNum)
      focusMinBy <$> parseFocuser
  
  parseFocusMaxBy :: Parser Focuser
  parseFocusMaxBy = do
      lexeme $ string "maxBy" >> notFollowedBy (satisfy isAlphaNum)
      focusMaxBy <$> parseFocuser
  
  parseFocusSortedLexBy :: Parser Focuser
  parseFocusSortedLexBy = do
      lexeme $ string "sortedLexBy" >> notFollowedBy (satisfy isAlphaNum)
      focusSortedLexBy <$> parseFocuser
  
  parseFocusMinLexBy :: Parser Focuser
  parseFocusMinLexBy = do
      lexeme $ string "minLexBy" >> notFollowedBy (satisfy isAlphaNum)
      focusMinLexBy <$> parseFocuser
  
  parseFocusMaxLexBy :: Parser Focuser
  parseFocusMaxLexBy = do
      lexeme $ string "maxLexBy" >> notFollowedBy (satisfy isAlphaNum)
      focusMaxLexBy <$> parseFocuser
  
  parseFocusAdd :: Parser Focuser
  parseFocusAdd = do
      symbol "add "
      focusTo . mappingAdd <$> rational
  
  parseFocusSub :: Parser Focuser
  parseFocusSub = do
      symbol "sub "
      focusTo . mappingSub <$> rational
  
  parseFocusMult :: Parser Focuser
  parseFocusMult = do
      symbol "mult "
      focusTo . mappingMult <$> rational
  
  parseFocusDiv :: Parser Focuser
  parseFocusDiv = do
      symbol "div "
      focusTo . mappingDiv <$> rational
  
  parseFocusPow :: Parser Focuser
  parseFocusPow = do
      symbol "pow "
      focusTo . mappingPow <$> integer
  
  parseFocusIf :: Parser Focuser
  parseFocusIf = do
      lexeme $ string "if" >> notFollowedBy (satisfy isAlphaNum)
      focusIf <$> parseFocuser
  
  parseFocusRegex :: Parser Focuser
  parseFocusRegex = do
      symbol "regex"
      focusRegex <$> stringLiteral
  
  parseFocusFilter :: Parser Focuser
  parseFocusFilter = do
      lexeme $ string "filter" >> notFollowedBy (satisfy isAlphaNum)
      focusFilter <$> parseFocuser
  
  parseFocusContains :: Parser Focuser
  parseFocusContains = do
      symbol "contains"
      focusContains <$> stringLiteral
  
  parseFocusStartsWith :: Parser Focuser
  parseFocusStartsWith = do
      symbol "startsWith"
      focusStartsWith <$> stringLiteral
  
  parseFocusEndsWith :: Parser Focuser
  parseFocusEndsWith = do
      symbol "endsWith"
      focusEndsWith <$> stringLiteral
  
  parseFocusAverage :: Parser Focuser
  parseFocusAverage = do
      symbol "average"
      def <- fromMaybe 0 <$> optional rational
      return $ focusAverage def
  
  parseFocusAtKey :: Parser Focuser
  parseFocusAtKey = do
      symbol "atKey"
      focusAtKey <$> stringLiteral
  
  parseFocusAtIdx :: Parser Focuser
  parseFocusAtIdx = do
      symbol "atIdx "
      focusAtIdx <$> integer
  
  parseFocusAll :: Parser Focuser
  parseFocusAll = do
      lexeme $ string "all" >> notFollowedBy (satisfy isAlphaNum)
      focusLogicMany and <$> parseFocuser
  
  parseFocusAny :: Parser Focuser
  parseFocusAny = do
      lexeme $ string "any" >> notFollowedBy (satisfy isAlphaNum)
      focusLogicMany or <$> parseFocuser
  
  parseFocusAnd :: Parser Focuser
  parseFocusAnd = do
      symbol "&&"
      p1 <- parseFocuser
      p2 <- parseFocuser
      return $ focusLogic2 (&&) p1 p2
  
  parseFocusOr :: Parser Focuser
  parseFocusOr = do
      symbol "||"
      p1 <- parseFocuser
      p2 <- parseFocuser
      return $ focusLogic2 (||) p1 p2
  
  parseFocusEq :: Parser Focuser
  parseFocusEq = do
      symbol "="
      p1 <- parseFocuser
      mp2 <- optional parseFocuser
      return $ case mp2 of
          Just p2 -> focusCompEq (==) p1 p2
          Nothing -> focusCompEq (==) focusId p1
  
  parseFocusNeq :: Parser Focuser
  parseFocusNeq = do
      symbol "!="
      p1 <- parseFocuser
      mp2 <- optional parseFocuser
      return $ case mp2 of
          Just p2 -> focusCompEq (/=) p1 p2
          Nothing -> focusCompEq (/=) focusId p1
  
  parseFocusLt :: Parser Focuser
  parseFocusLt = do
      symbol "<"
      p1 <- parseFocuser
      mp2 <- optional parseFocuser
      return $ case mp2 of
          Just p2 -> focusCompOrd (<) p1 p2
          Nothing -> focusCompOrd (<) focusId p1
  
  parseFocusGt :: Parser Focuser
  parseFocusGt = do
      symbol ">"
      p1 <- parseFocuser
      mp2 <- optional parseFocuser
      return $ case mp2 of
          Just p2 -> focusCompOrd (>) p1 p2
          Nothing -> focusCompOrd (>) focusId p1
  
  parseFocusLeq :: Parser Focuser
  parseFocusLeq = do
      symbol "<="
      p1 <- parseFocuser
      mp2 <- optional parseFocuser
      return $ case mp2 of
          Just p2 -> focusCompOrd (<=) p1 p2
          Nothing -> focusCompOrd (<=) focusId p1
  
  parseFocusGeq :: Parser Focuser
  parseFocusGeq = do
      symbol ">="
      p1 <- parseFocuser
      mp2 <- optional parseFocuser
      return $ case mp2 of
          Just p2 -> focusCompOrd (>=) p1 p2
          Nothing -> focusCompOrd (>=) focusId p1
  
  parseFocusLit :: Parser Focuser
  parseFocusLit = parseFocusString <|> parseFocusNumber
    where
      parseFocusString = focusConst <$> stringLiteral
      parseFocusNumber = focusConst . showRational <$> rational
  
  -- mapping parsers
  
  parseMapping :: Parser Mapping
  parseMapping = label "valid mapping" $ choice
      [ symbol "reverse" $> mappingReverse
      , symbol "len" $> mappingLength
      , parseMappingMap
      , parseMappingAppend
      , parseMappingPrepend
      , symbol "upper" $> mappingUpper
      , symbol "lower" $> mappingLower
      , between (symbol "(") (symbol ")") $ foldMappings <$> parseMappings
      , parseMappingAdd
      , parseMappingSub
      , parseMappingMult
      , parseMappingDiv
      , parseMappingPow
      , symbol "abs" $> mappingAbs
      , symbol "sign" $> mappingSign
      , parseMappingSlice
      , parseMappingSortLexBy
      , symbol "sortLex" $> mappingSortLexBy focusId
      , parseMappingSortBy
      , symbol "sort" $> mappingSortBy focusId
      , symbol "id" $> mappingId
      , parseMappingTo
      ]
  
  parseMappings :: Parser [Mapping]
  parseMappings = label "valid mapping stack" $ parseMapping `sepBy1` symbol ":"
  
  parseMappingMap :: Parser Mapping
  parseMappingMap = do
      lexeme $ string "map" >> notFollowedBy (satisfy isAlphaNum)
      mappingMap <$> parseMapping
  
  stringLiteral :: Parser Text
  stringLiteral = T.pack <$> label "string literal" (do
      between (char '"') (symbol "\"") $ many $ choice
          [ char '\\' >> anySingle
          , anySingleBut '"'
          ])
  
  parseMappingAppend :: Parser Mapping
  parseMappingAppend = do
      lexeme $ string "append" >> notFollowedBy (satisfy isAlphaNum)
      mappingAppend . foldFocusers <$> parseFocusers
  
  parseMappingPrepend :: Parser Mapping
  parseMappingPrepend = do
      lexeme $ string "prepend" >> notFollowedBy (satisfy isAlphaNum)
      mappingPrepend . foldFocusers <$> parseFocusers
  
  parseMappingAdd :: Parser Mapping
  parseMappingAdd = do
      symbol "add "
      mappingAdd <$> rational
  
  parseMappingSub :: Parser Mapping
  parseMappingSub = do
      symbol "sub "
      mappingSub <$> rational
  
  parseMappingMult :: Parser Mapping
  parseMappingMult = do
      symbol "mult "
      mappingMult <$> rational
  
  parseMappingDiv :: Parser Mapping
  parseMappingDiv = do
      symbol "div "
      mappingDiv <$> rational
  
  parseMappingPow :: Parser Mapping
  parseMappingPow = do
      symbol "pow "
      mappingPow <$> integer
  
  parseMappingSlice :: Parser Mapping
  parseMappingSlice = do
      ranges <- between (symbol "{") (symbol "}") (range `sepBy` symbol ",")
      return $ mappingSlice ranges
  
  parseMappingSortBy :: Parser Mapping
  parseMappingSortBy = do
      lexeme $ string "sortBy" >> notFollowedBy (satisfy isAlphaNum)
      mappingSortBy <$> parseFocuser
  
  parseMappingSortLexBy :: Parser Mapping
  parseMappingSortLexBy = do
      lexeme $ string "sortLexBy" >> notFollowedBy (satisfy isAlphaNum)
      mappingSortLexBy <$> parseFocuser
  
  parseMappingTo :: Parser Mapping
  parseMappingTo = do
      lexeme $ string "to" >> notFollowedBy (satisfy isAlphaNum)
      focuser <- foldFocusers <$> parseFocusers
      return $ mappingTo focuser
  