{-# LANGUAGE TemplateHaskell #-}
module TreeSitter.Language
( module TreeSitter.Language
, module TreeSitter.Symbol
) where
import Data.Ix (Ix)
import Data.List (mapAccumL)
import qualified Data.Set as Set
import Data.Traversable (for)
import Data.Word
import Foreign.C.String
import Foreign.Ptr
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import System.Directory
import System.FilePath.Posix
import TreeSitter.Symbol
data Language
foreign import ccall unsafe "ts_language_symbol_count" ts_language_symbol_count :: Ptr Language -> IO Word32
foreign import ccall unsafe "ts_language_symbol_name" ts_language_symbol_name :: Ptr Language -> TSSymbol -> IO CString
foreign import ccall unsafe "ts_language_symbol_type" ts_language_symbol_type :: Ptr Language -> TSSymbol -> IO Int
foreign import ccall unsafe "ts_language_symbol_for_name" ts_language_symbol_for_name :: Ptr Language -> CString -> Int -> Bool -> IO TSSymbol
mkSymbolDatatype :: Name -> Ptr Language -> Q [Dec]
mkSymbolDatatype :: Name -> Ptr Language -> Q [Dec]
mkSymbolDatatype name :: Name
name language :: Ptr Language
language = do
[(SymbolType, String)]
symbols <- [(SymbolType, String)] -> [(SymbolType, String)]
forall a. [(a, String)] -> [(a, String)]
renameDups ([(SymbolType, String)] -> [(SymbolType, String)])
-> ([(SymbolType, String)] -> [(SymbolType, String)])
-> [(SymbolType, String)]
-> [(SymbolType, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((SymbolType, String) -> (SymbolType, String))
-> [(SymbolType, String)] -> [(SymbolType, String)]
forall a b. (a -> b) -> [a] -> [b]
map ((,) (SymbolType -> String -> (SymbolType, String))
-> ((SymbolType, String) -> SymbolType)
-> (SymbolType, String)
-> String
-> (SymbolType, String)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SymbolType, String) -> SymbolType
forall a b. (a, b) -> a
fst ((SymbolType, String) -> String -> (SymbolType, String))
-> ((SymbolType, String) -> String)
-> (SymbolType, String)
-> (SymbolType, String)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (SymbolType -> String -> String) -> (SymbolType, String) -> String
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry SymbolType -> String -> String
symbolToName) ([(SymbolType, String)] -> [(SymbolType, String)])
-> ([(SymbolType, String)] -> [(SymbolType, String)])
-> [(SymbolType, String)]
-> [(SymbolType, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([(SymbolType, String)]
-> [(SymbolType, String)] -> [(SymbolType, String)]
forall a. [a] -> [a] -> [a]
++ [(SymbolType
Regular, "ParseError")]) ([(SymbolType, String)] -> [(SymbolType, String)])
-> Q [(SymbolType, String)] -> Q [(SymbolType, String)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [(SymbolType, String)] -> Q [(SymbolType, String)]
forall a. IO a -> Q a
runIO (Ptr Language -> IO [(SymbolType, String)]
languageSymbols Ptr Language
language)
Module _ modName :: ModName
modName <- Q Module
thisModule
let mkMatch :: t -> String -> MatchQ
mkMatch symbolType :: t
symbolType str :: String
str = PatQ -> BodyQ -> [DecQ] -> MatchQ
match (Name -> [PatQ] -> PatQ
conP (OccName -> NameFlavour -> Name
Name (String -> OccName
OccName String
str) (ModName -> NameFlavour
NameQ ModName
modName)) []) (ExpQ -> BodyQ
normalB [e|symbolType|]) []
Dec
datatype <- CxtQ
-> Name
-> [TyVarBndr]
-> Maybe Kind
-> [ConQ]
-> [DerivClauseQ]
-> DecQ
dataD ([Kind] -> CxtQ
forall (f :: * -> *) a. Applicative f => a -> f a
pure []) Name
name [] Maybe Kind
forall a. Maybe a
Nothing ((Name -> [BangTypeQ] -> ConQ) -> [BangTypeQ] -> Name -> ConQ
forall a b c. (a -> b -> c) -> b -> a -> c
flip Name -> [BangTypeQ] -> ConQ
normalC [] (Name -> ConQ)
-> ((SymbolType, String) -> Name) -> (SymbolType, String) -> ConQ
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Name
mkName (String -> Name)
-> ((SymbolType, String) -> String) -> (SymbolType, String) -> Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SymbolType, String) -> String
forall a b. (a, b) -> b
snd ((SymbolType, String) -> ConQ) -> [(SymbolType, String)] -> [ConQ]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(SymbolType, String)]
symbols)
[ Maybe DerivStrategy -> [PredQ] -> DerivClauseQ
derivClause Maybe DerivStrategy
forall a. Maybe a
Nothing ((Name -> PredQ) -> [Name] -> [PredQ]
forall a b. (a -> b) -> [a] -> [b]
map Name -> PredQ
conT [ ''Bounded, ''Enum, ''Eq, ''Ix, ''Ord, ''Show ]) ]
[Dec]
symbolInstance <- [d|
instance Symbol $(conT name) where
symbolType = $(lamCaseE (uncurry mkMatch <$> symbols)) |]
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Dec
datatype Dec -> [Dec] -> [Dec]
forall a. a -> [a] -> [a]
: [Dec]
symbolInstance)
renameDups :: [(a, String)] -> [(a, String)]
renameDups :: [(a, String)] -> [(a, String)]
renameDups = (Set String, [(a, String)]) -> [(a, String)]
forall a b. (a, b) -> b
snd ((Set String, [(a, String)]) -> [(a, String)])
-> ([(a, String)] -> (Set String, [(a, String)]))
-> [(a, String)]
-> [(a, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Set String -> (a, String) -> (Set String, (a, String)))
-> Set String -> [(a, String)] -> (Set String, [(a, String)])
forall (t :: * -> *) a b c.
Traversable t =>
(a -> b -> (a, c)) -> a -> t b -> (a, t c)
mapAccumL Set String -> (a, String) -> (Set String, (a, String))
forall a. Set String -> (a, String) -> (Set String, (a, String))
go Set String
forall a. Monoid a => a
mempty
where go :: Set String -> (a, String) -> (Set String, (a, String))
go done :: Set String
done (ty :: a
ty, name :: String
name) = let name' :: String
name' = String -> String
rename String
name in (String -> Set String -> Set String
forall a. Ord a => a -> Set a -> Set a
Set.insert String
name' Set String
done, (a
ty, String
name'))
where rename :: String -> String
rename name :: String
name | String
name String -> Set String -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set String
done = String -> String
rename (String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ "'")
| Bool
otherwise = String
name
addDependentFileRelative :: FilePath -> Q [Dec]
addDependentFileRelative :: String -> Q [Dec]
addDependentFileRelative relativeFile :: String
relativeFile = do
String
currentFilename <- Loc -> String
loc_filename (Loc -> String) -> Q Loc -> Q String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Q Loc
location
String
pwd <- IO String -> Q String
forall a. IO a -> Q a
runIO IO String
getCurrentDirectory
let invocationRelativePath :: String
invocationRelativePath = String -> String
takeDirectory (String
pwd String -> String -> String
</> String
currentFilename) String -> String -> String
</> String
relativeFile
String -> Q ()
addDependentFile String
invocationRelativePath
[Dec] -> Q [Dec]
forall (m :: * -> *) a. Monad m => a -> m a
return []
languageSymbols :: Ptr Language -> IO [(SymbolType, String)]
languageSymbols :: Ptr Language -> IO [(SymbolType, String)]
languageSymbols language :: Ptr Language
language = Ptr Language -> IO Word32
ts_language_symbol_count Ptr Language
language IO Word32
-> (Word32 -> IO [(SymbolType, String)])
-> IO [(SymbolType, String)]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ count :: Word32
count -> [TSSymbol]
-> (TSSymbol -> IO (SymbolType, String))
-> IO [(SymbolType, String)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for [0..Word32 -> TSSymbol
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32 -> Word32
forall a. Enum a => a -> a
pred Word32
count)] ((TSSymbol -> IO (SymbolType, String))
-> IO [(SymbolType, String)])
-> (TSSymbol -> IO (SymbolType, String))
-> IO [(SymbolType, String)]
forall a b. (a -> b) -> a -> b
$ \ symbol :: TSSymbol
symbol -> do
CString
cname <- Ptr Language -> TSSymbol -> IO CString
ts_language_symbol_name Ptr Language
language TSSymbol
symbol
String
name <- CString -> IO String
peekCString CString
cname
SymbolType
ty <- Int -> SymbolType
forall a. Enum a => Int -> a
toEnum (Int -> SymbolType) -> IO Int -> IO SymbolType
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Language -> TSSymbol -> IO Int
ts_language_symbol_type Ptr Language
language TSSymbol
symbol
(SymbolType, String) -> IO (SymbolType, String)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SymbolType
ty, String
name)