smh-0.2.1: src/Parsers.hs
{-# 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