-- Copyright (c) 2017 Uber Technologies, Inc.
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

module Database.Sql.Vertica.Parser.Token where


import Database.Sql.Vertica.Token
import Database.Sql.Vertica.Type
import Database.Sql.Vertica.Parser.Internal

import Database.Sql.Position

import qualified Text.Parsec as P
import qualified Text.Parsec.Pos as P

import           Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import           Data.ByteString.Lazy (ByteString)

import Data.String

import Data.Semigroup ((<>))


showTok :: (Token, Position, Position) -> String
showTok (t, _, _) = show t

posFromTok :: (Token, Position, Position) -> P.SourcePos
posFromTok (_, pos, _) = flip P.setSourceLine (fromEnum $ positionLine pos)
                       $ flip P.setSourceColumn (fromEnum $ positionColumn pos)
                       $ P.initialPos "-"

tokEqualsP :: Token -> Parser Range
tokEqualsP tok = P.token showTok posFromTok testTok
  where
    testTok (tok', s, e) =
        if tok == tok'
         then Just $ Range s e
         else Nothing

tokNotEqualsP :: Token -> Parser Range
tokNotEqualsP tok = P.token showTok posFromTok testTok
  where
    testTok (tok', s, e) =
        if tok /= tok'
         then Just $ Range s e
         else Nothing

testNameTok :: (Token, Position, Position) -> Maybe (Text, Range)
testNameTok (tok, s, e) =
  case tok of
    TokWord _ name -> Just (name, Range s e)
    _ -> Nothing

typeNameP :: Parser (Text, Range)
typeNameP = P.token showTok posFromTok testNameTok

nodeNameP :: Parser (Text, Range)
nodeNameP = P.token showTok posFromTok testNameTok

databaseNameP :: Parser (Text, Range)
databaseNameP = P.token showTok posFromTok testNameTok

userNameP :: Parser (Text, Range)
userNameP = P.token showTok posFromTok testNameTok

paramNameP :: Parser (Text, Range)
paramNameP = P.token showTok posFromTok testNameTok

windowNameP :: Parser (Text, Range)
windowNameP = P.token showTok posFromTok testNameTok

parserNameP :: Parser (Text, Range)
parserNameP = P.token showTok posFromTok testNameTok

datePartP :: Parser (Text, Range)
datePartP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord _ name | TL.toLower name `elem` parts -> Just (TL.toLower name, Range s e)
        _ -> Nothing

    parts = [ "year", "yy", "yyyy"
            , "quarter", "qq", "q"
            , "month", "mm", "m"
            , "day", "dd", "d", "dy", "dayofyear", "y"
            , "week", "wk", "ww"
            , "hour", "hh"
            , "minute", "mi", "n"
            , "second", "ss", "s"
            , "millisecond", "ms"
            , "microsecond", "mcs", "us"
            ]

schemaNameP :: Parser (Text, Range)
schemaNameP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord True name -> Just (name, Range s e)
        TokWord False name
            | wordCanBeSchemaName (wordInfo name) -> Just (name, Range s e)

        _ -> Nothing


tableNameP :: Parser (Text, Range)
tableNameP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord True name -> Just (name, Range s e)
        TokWord False name
            | wordCanBeTableName (wordInfo name) -> Just (name, Range s e)

        _ -> Nothing


projectionNameP :: Parser (Text, Range)
projectionNameP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord True name -> Just (name, Range s e)
        TokWord False name
            | wordCanBeTableName (wordInfo name) -> Just (name, Range s e)

        _ -> Nothing


columnNameP :: Parser (Text, Range)
columnNameP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord True name -> Just (name, Range s e)
        TokWord False name
            | wordCanBeColumnName (wordInfo name) -> Just (name, Range s e)

        _ -> Nothing


functionNameP :: Parser (Text, Range)
functionNameP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord True name -> Just (name, Range s e)
        TokWord False name
            | wordCanBeFunctionName (wordInfo name) ->
                Just (name, Range s e)

        _ -> Nothing

constraintNameP :: Parser (Text, Range)
constraintNameP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        -- constraint names MAY be quoted.
        TokWord _ name -> Just (name, Range s e)
        _ -> Nothing

keywordP :: Text -> Parser Range
keywordP keyword = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokWord False name
            | name == keyword -> Just (Range s e)

        _ -> Nothing

fieldTypeP :: Parser (Text, Range)
fieldTypeP = P.token showTok posFromTok testTok
  where
    isField :: (Eq s, IsString s) => s -> Bool
    isField = flip elem
        [ "century", "day", "decade", "doq", "dow", "doy", "epoch"
        , "hour", "isodow", "isoweek", "isoyear", "microseconds"
        , "millennium", "milliseconds", "minute", "month", "quarter"
        , "second", "time zone", "timezone_hour", "timezone_minute"
        , "week", "year"
        ]

    testTok (tok, s, e) = case tok of
        TokWord _ field | isField field -> Just (field, Range s e)
        TokString field | isField field -> Just (TL.decodeUtf8 field, Range s e)
        _ -> Nothing

periodP :: Parser (Text, Range)
periodP = P.token showTok posFromTok testTok
  where
    isPeriod :: (Eq s, IsString s) => s -> Bool
    isPeriod = flip elem [ "day", "hour", "minute", "month", "second", "year" ]

    testTok (tok, s, e) = case tok of
        TokWord _ period | isPeriod period -> Just (period, Range s e)
        TokString period | isPeriod period -> Just (TL.decodeUtf8 period, Range s e)
        _ -> Nothing

stringP :: Parser (ByteString, Range)
stringP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokString string -> Just (string, Range s e)
        _ -> Nothing

numberP :: Parser (Text, Range)
numberP = P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) = case tok of
        TokNumber number -> Just (number, Range s e)
        _ -> Nothing

encodingTypeP :: Parser (Encoding Range)
encodingTypeP =  P.token showTok posFromTok testTok
  where
    testTok (tok, s, e) =
        let r = Range s e
         in case tok of
                TokWord False "auto" -> Just (EncodingAuto r)
                TokWord False "block_dict" -> Just (EncodingBlockDict r)
                TokWord False "blockdict_comp" -> Just (EncodingBlockDictComp r)
                TokWord False "bzip_comp" -> Just (EncodingBZipComp r)
                TokWord False "commondelta_comp" -> Just (EncodingCommonDeltaComp r)
                TokWord False "deltarange_comp" -> Just (EncodingDeltaRangeComp r)
                TokWord False "deltaval" -> Just (EncodingDeltaVal r)
                TokWord False "gcddelta" -> Just (EncodingGCDDelta r)
                TokWord False "gzip_comp" -> Just (EncodingGZipComp r)
                TokWord False "rle" -> Just (EncodingRLE r)
                TokWord False "none" -> Just (EncodingNone r)

                _ -> Nothing

dotP :: Parser Range
dotP = symbolP "."

equalP :: Parser Range
equalP = symbolP "="

symbolP :: Text -> Parser Range
symbolP op = tokEqualsP $ TokSymbol op

starP :: Parser Range
starP = symbolP "*"

openP :: Parser Range
openP = symbolP "("

closeP :: Parser Range
closeP = symbolP ")"

openBracketP :: Parser Range
openBracketP = symbolP "["

closeBracketP :: Parser Range
closeBracketP = symbolP "]"

castP :: Parser Range
castP = keywordP "cast"

castOpP :: Parser Range
castOpP = symbolP "::"

minusP :: Parser Range
minusP = symbolP "-"

abortP :: Parser Range
abortP = keywordP "abort"

accessP :: Parser Range
accessP = keywordP "access"

accessRankP :: Parser Range
accessRankP = keywordP "accessrank"

addP :: Parser Range
addP = keywordP "add"

aggregateP :: Parser Range
aggregateP = keywordP "aggregate"

allP :: Parser Range
allP = keywordP "all"

alterP :: Parser Range
alterP = keywordP "alter"

analyticP :: Parser Range
analyticP = keywordP "analytic"

andP :: Parser Range
andP = keywordP "and"

anyP :: Parser Range
anyP = keywordP "any"

arrayP :: Parser Range
arrayP = keywordP "array"

asP :: Parser Range
asP = keywordP "as"

ascP :: Parser Range
ascP = keywordP "asc"

atP :: Parser Range
atP = keywordP "at"

authorizationP :: Parser Range
authorizationP = keywordP "authorization"

autoP :: Parser Range
autoP = keywordP "auto"

beginP :: Parser Range
beginP = keywordP "begin"

bestP :: Parser Range
bestP = keywordP "best"

betweenP :: Parser Range
betweenP = keywordP "between"

byP :: Parser Range
byP = keywordP "by"

bytesP :: Parser Range
bytesP = keywordP "bytes"

bzipP :: Parser Range
bzipP = keywordP "bzip"

cascadeP :: Parser Range
cascadeP = keywordP "cascade"

caseP :: Parser Range
caseP = keywordP "case"

checkP :: Parser Range
checkP = keywordP "check"

colSizesP :: Parser Range
colSizesP = keywordP "colsizes"

columnP :: Parser Range
columnP = keywordP "column"

commaP :: Parser Range
commaP = symbolP ","

commitP :: Parser Range
commitP = keywordP "commit"

committedP :: Parser Range
committedP = keywordP "committed"

connectP :: Parser Range
connectP = keywordP "connect"

constraintP :: Parser Range
constraintP = keywordP "constraint"

copyP :: Parser Range
copyP = keywordP "copy"

createP :: Parser Range
createP = keywordP "create"

crossP :: Parser Range
crossP = keywordP "cross"

currentP :: Parser Range
currentP = keywordP "current"

currentDatabaseP :: Parser (Text, Range)
currentDatabaseP = ("current_database",) <$> keywordP "current_database"

currentDateP :: Parser (Text, Range)
currentDateP = ("current_date",) <$> keywordP "current_date"

currentSchemaP :: Parser (Text, Range)
currentSchemaP = ("current_schema",) <$> keywordP "current_schema"

currentTimeP :: Parser (Text, Range)
currentTimeP = ("current_time",) <$> keywordP "current_time"

currentTimestampP :: Parser (Text, Range)
currentTimestampP = ("current_timestamp",) <$> keywordP "current_timestamp"

currentUserP :: Parser (Text, Range)
currentUserP = ("current_user",) <$> keywordP "current_user"

dataP :: Parser Range
dataP = keywordP "data"

dateDiffP :: Parser Range
dateDiffP = keywordP "datediff"

defaultP :: Parser Range
defaultP = keywordP "default"

deleteP :: Parser Range
deleteP = keywordP "delete"

delimiterP :: Parser Range
delimiterP = keywordP "delimiter"

descP :: Parser Range
descP = keywordP "desc"

directP :: Parser Range
directP = keywordP "direct"

disableP :: Parser Range
disableP = keywordP "disable"

disabledP :: Parser Range
disabledP = keywordP "disabled"

disconnectP :: Parser Range
disconnectP = keywordP "disconnect"

distinctP :: Parser Range
distinctP = keywordP "distinct"

doubleP :: Parser Range
doubleP = keywordP "double"

dropP :: Parser Range
dropP = keywordP "drop"

elseP :: Parser Range
elseP = keywordP "else"

enableP :: Parser Range
enableP = keywordP "enable"

enabledP :: Parser Range
enabledP = keywordP "enabled"

enclosedP :: Parser Range
enclosedP = keywordP "enclosed"

encodingP :: Parser Range
encodingP = keywordP "encoding"

endP :: Parser Range
endP = keywordP "end"

enforceLengthP :: Parser Range
enforceLengthP = keywordP "enforcelength"

errorP :: Parser Range
errorP = keywordP "error"

escapeP :: Parser Range
escapeP = keywordP "escape"

exceptP :: Parser Range
exceptP = keywordP "except"

exceptionsP :: Parser Range
exceptionsP = keywordP "exceptions"

excludeP :: Parser Range
excludeP = keywordP "exclude"

excludingP :: Parser Range
excludingP = keywordP "excluding"

existsP :: Parser Range
existsP = keywordP "exists"

explainP :: Parser Range
explainP = keywordP "explain"

exportP :: Parser Range
exportP = keywordP "export"

externalP :: Parser Range
externalP = keywordP "external"

extractP :: Parser Range
extractP = keywordP "extract"

falseP :: Parser Range
falseP = keywordP "false"

fillerP :: Parser Range
fillerP = keywordP "filler"

filterP :: Parser Range
filterP = keywordP "filter"

firstP :: Parser Range
firstP = keywordP "first"

fixedWidthP :: Parser Range
fixedWidthP = keywordP "fixedwidth"

followingP :: Parser Range
followingP = keywordP "following"

forP :: Parser Range
forP = keywordP "for"

foreignP :: Parser Range
foreignP = keywordP "foreign"

formatP :: Parser Range
formatP = keywordP "format"

fromP :: Parser Range
fromP = keywordP "from"

fullP :: Parser Range
fullP = keywordP "full"

functionP :: Parser Range
functionP = keywordP "function"

globalP :: Parser Range
globalP =  keywordP "global"

grantP :: Parser Range
grantP = keywordP "grant"

groupP :: Parser Range
groupP = keywordP "group"

gzipP :: Parser Range
gzipP = keywordP "gzip"

havingP :: Parser Range
havingP = keywordP "having"

ifP :: Parser Range
ifP = keywordP "if"

ignoreP :: Parser Range
ignoreP = keywordP "ignore"

iLikeP :: Parser Range
iLikeP = keywordP "ilike"

iLikeBP :: Parser Range
iLikeBP = keywordP "ilikeb"

inP :: Parser Range
inP = keywordP "in"

includeP :: Parser Range
includeP = keywordP "include"

includingP :: Parser Range
includingP = keywordP "including"

innerP :: Parser Range
innerP = keywordP "inner"

insertP :: Parser Range
insertP = keywordP "insert"

intersectP :: Parser Range
intersectP = keywordP "intersect"

intervalP :: Parser Range
intervalP = keywordP "interval"

intoP :: Parser Range
intoP = keywordP "into"

isP :: Parser Range
isP = keywordP "is"

isnullP :: Parser Range
isnullP = keywordP "isnull"

isolationP :: Parser Range
isolationP = keywordP "isolation"

joinP :: Parser Range
joinP = keywordP "join"

keyP :: Parser Range
keyP = keywordP "key"

ksafeP :: Parser Range
ksafeP = keywordP "ksafe"

lastP :: Parser Range
lastP = keywordP "last"

leftP :: Parser Range
leftP = keywordP "left"

levelP :: Parser Range
levelP = keywordP "level"

likeP :: Parser Range
likeP = keywordP "like"

likeBP :: Parser Range
likeBP = keywordP "likeB"

limitP :: Parser Range
limitP = keywordP "limit"

localP :: Parser Range
localP =  keywordP "local"

localTimeP :: Parser (Text, Range)
localTimeP = ("localtime",) <$> keywordP "localtime"

localTimestampP :: Parser (Text, Range)
localTimestampP = ("localtimestamp",) <$> keywordP "localtimestamp"

longP :: Parser Range
longP = keywordP "long"

lzoP :: Parser Range
lzoP = keywordP "lzo"

matchedP :: Parser Range
matchedP = keywordP "matched"

mergeP :: Parser Range
mergeP = keywordP "merge"

nameP :: Parser Range
nameP = keywordP "name"

nativeP :: Parser Range
nativeP = keywordP "native"

naturalP :: Parser Range
naturalP = keywordP "natural"

noP :: Parser Range
noP = keywordP "no"

nodeP :: Parser Range
nodeP = keywordP "node"

nodesP :: Parser Range
nodesP = keywordP "nodes"

notP :: Parser Range
notP = keywordP "not"

notnullP :: Parser Range
notnullP = keywordP "notnull"

nullsP :: Parser Range
nullsP = keywordP "nulls"

nullsequalP :: Parser Range
nullsequalP = keywordP "nullsequal"

nullP :: Parser Range
nullP = keywordP "null"

nullColsP :: Parser Range
nullColsP = keywordP "nullcols"

offsetP :: Parser Range
offsetP = keywordP "offset"

onP :: Parser Range
onP = keywordP "on"

onlyP :: Parser Range
onlyP = keywordP "only"

optionP :: Parser Range
optionP = keywordP "option"

orP :: Parser Range
orP = keywordP "or"

orcP :: Parser Range
orcP = keywordP "orc"

orderP :: Parser Range
orderP = keywordP "order"

overlapsP :: Parser Range
overlapsP = keywordP "overlaps"

outerP :: Parser Range
outerP = keywordP "outer"

overP :: Parser Range
overP = keywordP "over"

parametersP :: Parser Range
parametersP = keywordP "parameters"

parquetP :: Parser Range
parquetP = keywordP "parquet"

parserP :: Parser Range
parserP = keywordP "parser"

partitionP :: Parser Range
partitionP = keywordP "partition"

passwordP :: Parser Range
passwordP = keywordP "password"

poolP :: Parser Range
poolP = keywordP "pool"

policyP :: Parser Range
policyP = keywordP "policy"

precedingP :: Parser Range
precedingP = keywordP "preceding"

precisionP :: Parser Range
precisionP = keywordP "precision"

preserveP :: Parser Range
preserveP = keywordP "preserve"

primaryP :: Parser Range
primaryP = keywordP "primary"

privilegesP :: Parser Range
privilegesP = keywordP "privileges"

projectionP :: Parser Range
projectionP = keywordP "projection"

projectionsP :: Parser Range
projectionsP = keywordP "projections"

rangeP :: Parser Range
rangeP = keywordP "range"

readP :: Parser Range
readP = keywordP "read"

recordP :: Parser Range
recordP = keywordP "record"

referencesP :: Parser Range
referencesP = keywordP "references"

rejectedP :: Parser Range
rejectedP = keywordP "rejected"

rejectMaxP :: Parser Range
rejectMaxP = keywordP "rejectmax"

renameP :: Parser Range
renameP = keywordP "rename"

repeatableP :: Parser Range
repeatableP = keywordP "repeatable"

replaceP :: Parser Range
replaceP = keywordP "replace"

resourceP :: Parser Range
resourceP = keywordP "resource"

restrictP :: Parser Range
restrictP = keywordP "restrict"

revokeP :: Parser Range
revokeP = keywordP "revoke"

rightP :: Parser Range
rightP = keywordP "right"

rollbackP :: Parser Range
rollbackP = keywordP "rollback"

rowP :: Parser Range
rowP = keywordP "row"

rowsP :: Parser Range
rowsP = keywordP "rows"

schemaP :: Parser Range
schemaP = keywordP "schema"

segmentedP :: Parser Range
segmentedP = keywordP "segmented"

selectP :: Parser Range
selectP = keywordP "select"

semicolonP :: Parser Range
semicolonP = symbolP ";"

notSemicolonP :: Parser Range
notSemicolonP = tokNotEqualsP $ TokSymbol ";"

setP :: Parser Range
setP = keywordP "set"

serializableP :: Parser Range
serializableP = keywordP "serializable"

sessionP :: Parser Range
sessionP = keywordP "session"

sessionUserP :: Parser (Text, Range)
sessionUserP = ("session_user",) <$> keywordP "session_user"

showP :: Parser Range
showP = keywordP "show"

skipP :: Parser Range
skipP = keywordP "skip"

sourceP :: Parser Range
sourceP = keywordP "source"

startP :: Parser Range
startP = keywordP "start"

stdinP :: Parser Range
stdinP = keywordP "stdin"

stdoutP :: Parser Range
stdoutP = keywordP "stdout"

storageP :: Parser Range
storageP = keywordP "storage"

streamP :: Parser Range
streamP = keywordP "stream"

sysDateP :: Parser (Text, Range)
sysDateP = ("sysdate",) <$> keywordP "sysdate"

tableP :: Parser Range
tableP = keywordP "table"

temporaryP :: Parser Range
temporaryP = keywordP "temporary" P.<|> keywordP "temp"

terminatorP :: Parser Range
terminatorP = keywordP "terminator"

thenP :: Parser Range
thenP = keywordP "then"

timeseriesP :: Parser Range
timeseriesP = keywordP "timeseries"

timestampP :: Parser Range
timestampP = keywordP "timestamp" P.<|> do
    r <- keywordP "time"
    r' <- keywordP "stamp"
    return $ r <> r'

timezoneP :: Parser Range
timezoneP = keywordP "timezone" P.<|> do
    r <- keywordP "time"
    r' <- keywordP "zone"
    return $ r <> r'

toleranceP :: Parser Range
toleranceP = keywordP "tolerance"

trailingP :: Parser Range
trailingP = keywordP "trailing"

transactionP :: Parser Range
transactionP = keywordP "transaction"

transformP :: Parser Range
transformP = keywordP "transform"

trickleP :: Parser Range
trickleP = keywordP "trickle"

trimP :: Parser Range
trimP = keywordP "trim"

trueP :: Parser Range
trueP = keywordP "true"

truncateP :: Parser Range
truncateP = keywordP "truncate"

toP :: Parser Range
toP = keywordP "to"

unboundedP :: Parser Range
unboundedP = keywordP "unbounded"

uncommittedP :: Parser Range
uncommittedP = keywordP "uncommitted"

uncompressedP :: Parser Range
uncompressedP = keywordP "uncompressed"

unionP :: Parser Range
unionP = keywordP "union"

uniqueP :: Parser Range
uniqueP = keywordP "unique"

unknownP :: Parser Range
unknownP = keywordP "unknown"

unsegmentedP :: Parser Range
unsegmentedP = keywordP "unsegmented"

updateP :: Parser Range
updateP = keywordP "update"

userP :: Parser (Text, Range)
userP = ("user",) <$> keywordP "user"

usingP :: Parser Range
usingP = keywordP "using"

valuesP :: Parser Range
valuesP = keywordP "values"

varBinaryP :: Parser (Text, Range)
varBinaryP = ("varbinary", ) <$> keywordP "varbinary"

varCharP :: Parser (Text, Range)
varCharP = ("varchar", ) <$> keywordP "varchar"

verticaP :: Parser Range
verticaP = keywordP "vertica"

viewP :: Parser Range
viewP = keywordP "view"

whenP :: Parser Range
whenP = keywordP "when"

whereP :: Parser Range
whereP = keywordP "where"

windowP :: Parser Range
windowP = keywordP "window"

withP :: Parser Range
withP = keywordP "with"

withoutP :: Parser Range
withoutP = keywordP "without"

workP :: Parser Range
workP = keywordP "work"

writeP :: Parser Range
writeP = keywordP "write"

likeOpP :: Parser Range
likeOpP = symbolP "~~"

iLikeOpP :: Parser Range
iLikeOpP = symbolP "~~*"

notLikeOpP :: Parser Range
notLikeOpP = symbolP "!~~"

notILikeOpP :: Parser Range
notILikeOpP = symbolP "!~~*"

regexMatchesP :: Parser Range
regexMatchesP = symbolP "~"

notRegexMatchesP :: Parser Range
notRegexMatchesP = symbolP "!~"

regexIgnoreCaseMatchesP :: Parser Range
regexIgnoreCaseMatchesP = symbolP "~*"

notRegexIgnoreCaseMatchesP :: Parser Range
notRegexIgnoreCaseMatchesP = symbolP "!~*"

inequalityOpP :: Parser (Text, Range)
inequalityOpP = P.token showTok posFromTok testTok
  where
    -- <=> is actually an equality, but it seems to parse here
    testTok (TokSymbol op, s, e)
        | op `elem` ["<", ">", "<>", "<=>", "!="] = Just (op, Range s e)

    testTok _ = Nothing

equalityOpP :: Parser (Text, Range)
equalityOpP = P.token showTok posFromTok testTok
  where
    testTok (TokSymbol op, s, e)
        | op `elem` ["<=", ">=", "="] = Just (op, Range s e)

    testTok _ = Nothing