{-# LANGUAGE DeriveLift, LambdaCase, ScopedTypeVariables #-}
module TreeSitter.Symbol
( TSSymbol
, fromTSSymbol
, SymbolType(..)
, Symbol(..)
, symbolToName
, toHaskellCamelCaseIdentifier
, toHaskellPascalCaseIdentifier
, escapeOperatorPunctuation
, camelCase
, capitalize
) where
import Data.Char (isAlpha, isControl, toUpper)
import Data.Function ((&))
import qualified Data.HashSet as HashSet
import Data.Ix (Ix)
import Data.List.Split (condense, split, whenElt)
import Data.Word (Word16)
import Language.Haskell.TH.Syntax
type TSSymbol = Word16
fromTSSymbol :: forall symbol. Symbol symbol => TSSymbol -> symbol
fromTSSymbol symbol = toEnum (min (fromIntegral symbol) (fromEnum (maxBound :: symbol)))
data SymbolType = Regular | Anonymous | Auxiliary
deriving (Enum, Eq, Lift, Ord, Show)
class (Bounded s, Enum s, Ix s, Ord s, Show s) => Symbol s where
symbolType :: s -> SymbolType
symbolToName :: SymbolType -> String -> String
symbolToName ty name
= prefixHidden name
& toWords
& filter (not . all (== '_'))
& map escapeOperatorPunctuation
& (>>= capitalize)
& (prefix ++)
where
toWords = split (condense (whenElt (not . isAlpha)))
prefixHidden s@('_':_) = "Hidden" ++ s
prefixHidden s = s
prefix = case ty of
Regular -> ""
Anonymous -> "Anon"
Auxiliary -> "Aux"
toHaskellCamelCaseIdentifier :: String -> String
toHaskellCamelCaseIdentifier = addTickIfNecessary . escapeOperatorPunctuation . camelCase
addTickIfNecessary :: String -> String
addTickIfNecessary s
| HashSet.member s reservedNames = s <> "'"
| otherwise = s
where
reservedNames :: HashSet.HashSet String
reservedNames = HashSet.fromList ["type", "module", "data"]
toHaskellPascalCaseIdentifier :: String -> String
toHaskellPascalCaseIdentifier = addTickIfNecessary . capitalize . escapeOperatorPunctuation . camelCase
escapeOperatorPunctuation :: String -> String
escapeOperatorPunctuation = concatMap $ \case
'{' -> "LBrace"
'}' -> "RBrace"
'(' -> "LParen"
')' -> "RParen"
'.' -> "Dot"
':' -> "Colon"
',' -> "Comma"
'|' -> "Pipe"
';' -> "Semicolon"
'*' -> "Star"
'&' -> "Ampersand"
'=' -> "Equal"
'<' -> "LAngle"
'>' -> "RAngle"
'[' -> "LBracket"
']' -> "RBracket"
'+' -> "Plus"
'-' -> "Minus"
'/' -> "Slash"
'\\' -> "Backslash"
'^' -> "Caret"
'!' -> "Bang"
'%' -> "Percent"
'@' -> "At"
'~' -> "Tilde"
'?' -> "Question"
'`' -> "Backtick"
'#' -> "Hash"
'$' -> "Dollar"
'"' -> "DQuote"
'\'' -> "SQuote"
'\t' -> "Tab"
'\n' -> "LF"
'\r' -> "CR"
other
| isControl other -> escapeOperatorPunctuation (show other)
| otherwise -> [other]
camelCase :: String -> String
camelCase = foldr appender mempty
where
appender :: Char -> String -> String
appender '_' cs = capitalize cs
appender c cs = c : cs
capitalize :: String -> String
capitalize (c:cs) = toUpper c : cs
capitalize [] = []