gigaparsec-0.2.4.1: src/Text/Gigaparsec/Internal/Token/Names.hs
{-# LANGUAGE Safe #-}
{-# LANGUAGE OverloadedLists #-}
{-# OPTIONS_HADDOCK hide #-}
module Text.Gigaparsec.Internal.Token.Names (
Names, mkNames,
identifier, identifier',
userDefinedOperator, userDefinedOperator',
lexeme
) where
import Text.Gigaparsec (Parsec, empty, (<:>), atomic)
import Text.Gigaparsec.Char (stringOfMany, satisfy)
import Text.Gigaparsec.Errors.Combinator ((<?>), unexpectedWhen)
import Data.Set qualified as Set (member, map)
import Text.Gigaparsec.Token.Descriptions (
SymbolDesc(SymbolDesc, hardKeywords, hardOperators, caseSensitive),
NameDesc(NameDesc, identifierStart, identifierLetter,
operatorStart, operatorLetter),
CharPredicate
)
import Data.Char (toLower)
import Text.Gigaparsec.Token.Errors (
ErrorConfig (labelNameIdentifier, unexpectedNameIllegalIdentifier, labelNameOperator, unexpectedNameIllegalOperator, filterNameIllFormedIdentifier, filterNameIllFormedOperator)
)
import Text.Gigaparsec.Internal.Token.Errors (filterS)
-- TODO: primes are gross, better way?
type Names :: *
data Names = Names { identifier :: !(Parsec String)
, identifier' :: !(CharPredicate -> Parsec String)
, userDefinedOperator :: !(Parsec String)
, userDefinedOperator' :: !(CharPredicate -> Parsec String)
}
mkNames :: NameDesc -> SymbolDesc -> ErrorConfig -> Names
mkNames NameDesc{..} symbolDesc@SymbolDesc{..} !err = Names {..}
where
!isReserved = isReservedName symbolDesc
!identifier =
keyOrOp identifierStart identifierLetter isReserved (labelNameIdentifier err) (unexpectedNameIllegalIdentifier err)
identifier' start = filterS (filterNameIllFormedIdentifier err) (startsWith start) identifier
!userDefinedOperator =
keyOrOp operatorStart operatorLetter (flip Set.member hardOperators) (labelNameOperator err) (unexpectedNameIllegalOperator err)
userDefinedOperator' start = filterS (filterNameIllFormedOperator err) (startsWith start) userDefinedOperator
keyOrOp :: CharPredicate -> CharPredicate -> (String -> Bool) -> String -> (String -> String) -> Parsec String
keyOrOp start letter illegal name unexpectedIllegal =
atomic (unexpectedWhen cond (complete start letter)) <?> [name]
where cond x
| illegal x = Just (unexpectedIllegal x)
| otherwise = Nothing
trailer :: CharPredicate -> Parsec String
trailer = maybe (pure "") stringOfMany
complete :: CharPredicate -> CharPredicate -> Parsec String
complete (Just start) letter = satisfy start <:> trailer letter
complete Nothing _ = empty
startsWith :: CharPredicate -> String -> Bool
startsWith Nothing _ = True
startsWith (Just _) [] = False
startsWith (Just p) (c:_) = p c
lexeme :: (forall a. Parsec a -> Parsec a) -> Names -> Names
lexeme lexe Names{..} = Names { identifier = lexe identifier
, identifier' = lexe . identifier'
, userDefinedOperator = lexe userDefinedOperator
, userDefinedOperator' = lexe . userDefinedOperator'
}
isReservedName :: SymbolDesc -> String -> Bool
isReservedName SymbolDesc{..}
| caseSensitive = flip Set.member hardKeywords
| otherwise = flip Set.member lowerHardKeywords . allLower
where allLower = map toLower
lowerHardKeywords = Set.map allLower hardKeywords