1
1
mirror of https://github.com/github/semantic.git synced 2024-12-24 07:25:44 +03:00
semantic/app/Main.hs

151 lines
6.5 KiB
Haskell
Raw Normal View History

2015-11-18 01:44:16 +03:00
module Main where
2015-11-27 20:22:05 +03:00
import Categorizable
import Diff
2015-11-27 20:22:05 +03:00
import Interpreter
import Syntax
2015-12-03 05:40:34 +03:00
import Range
2015-12-01 19:33:16 +03:00
import Split
2015-11-27 20:41:38 +03:00
import Term
2015-11-27 20:42:00 +03:00
import Unified
import Control.Comonad.Cofree
2015-11-27 17:19:09 +03:00
import qualified Data.Map as Map
2015-12-07 22:34:58 +03:00
import qualified Data.ByteString.Char8 as B1
import qualified Data.ByteString.Lazy as B2
2015-12-01 19:33:10 +03:00
import Data.Set hiding (split)
2015-12-01 19:15:13 +03:00
import Options.Applicative
2015-12-03 23:10:49 +03:00
import System.FilePath
2015-11-24 21:15:45 +03:00
import Foreign
2015-11-25 00:40:47 +03:00
import Foreign.C
2015-11-24 21:15:45 +03:00
import Foreign.C.Types
2015-11-26 22:03:53 +03:00
data TSLanguage = TsLanguage deriving (Show, Eq)
2015-11-25 18:06:14 +03:00
foreign import ccall "prototype/doubt-difftool/doubt-difftool-Bridging-Header.h ts_language_c" ts_language_c :: IO (Ptr TSLanguage)
2015-12-03 23:34:41 +03:00
foreign import ccall "prototype/doubt-difftool/doubt-difftool-Bridging-Header.h ts_language_javascript" ts_language_javascript :: IO (Ptr TSLanguage)
2015-11-26 22:03:53 +03:00
data TSDocument = TsDocument deriving (Show, Eq)
2015-11-25 18:06:14 +03:00
foreign import ccall "prototype/External/tree-sitter/include/tree_sitter/runtime.h ts_document_make" ts_document_make :: IO (Ptr TSDocument)
foreign import ccall "prototype/External/tree-sitter/include/tree_sitter/runtime.h ts_document_set_language" ts_document_set_language :: Ptr TSDocument -> Ptr TSLanguage -> IO ()
foreign import ccall "prototype/External/tree-sitter/include/tree_sitter/runtime.h ts_document_set_input_string" ts_document_set_input_string :: Ptr TSDocument -> CString -> IO ()
foreign import ccall "prototype/External/tree-sitter/include/tree_sitter/runtime.h ts_document_parse" ts_document_parse :: Ptr TSDocument -> IO ()
foreign import ccall "prototype/External/tree-sitter/include/tree_sitter/runtime.h ts_document_free" ts_document_free :: Ptr TSDocument -> IO ()
2015-11-24 21:23:29 +03:00
2015-11-26 02:32:05 +03:00
data TSLength = TsLength { bytes :: CSize, chars :: CSize }
2015-11-26 22:03:53 +03:00
deriving (Show, Eq)
2015-11-25 00:39:31 +03:00
2015-11-26 02:32:05 +03:00
data TSNode = TsNode { _data :: Ptr (), offset :: TSLength }
2015-11-26 22:03:53 +03:00
deriving (Show, Eq)
2015-11-25 00:39:42 +03:00
instance Storable TSNode where
2015-11-26 02:32:17 +03:00
alignment n = 24
sizeOf n = 24
2015-11-27 00:21:54 +03:00
peek p = error "Haskell code should never read TSNode values directly."
poke p n = error "Haskell code should never write TSNode values directly."
2015-11-25 00:39:42 +03:00
foreign import ccall "app/bridge.h ts_document_root_node_p" ts_document_root_node_p :: Ptr TSDocument -> Ptr TSNode -> IO ()
2015-11-25 20:24:07 +03:00
foreign import ccall "app/bridge.h ts_node_p_name" ts_node_p_name :: Ptr TSNode -> Ptr TSDocument -> IO CString
foreign import ccall "app/bridge.h ts_node_p_named_child_count" ts_node_p_named_child_count :: Ptr TSNode -> IO CSize
foreign import ccall "app/bridge.h ts_node_p_named_child" ts_node_p_named_child :: Ptr TSNode -> CSize -> Ptr TSNode -> IO CSize
foreign import ccall "app/bridge.h ts_node_p_pos_chars" ts_node_p_pos_chars :: Ptr TSNode -> IO CSize
foreign import ccall "app/bridge.h ts_node_p_size_chars" ts_node_p_size_chars :: Ptr TSNode -> IO CSize
2015-12-01 23:04:23 +03:00
foreign import ccall "app/bridge.h ts_node_p_start_point" ts_node_p_start_point :: Ptr TSNode -> IO CSize
foreign import ccall "app/bridge.h ts_node_p_end_point" ts_node_p_end_point :: Ptr TSNode -> IO CSize
2015-11-24 22:08:08 +03:00
2015-12-01 19:08:04 +03:00
data Output = Unified | Split
2015-12-01 19:15:05 +03:00
data Argument = Argument { output :: Output, sourceA :: FilePath, sourceB :: FilePath }
2015-12-01 19:08:04 +03:00
2015-12-01 19:15:13 +03:00
arguments :: Parser Argument
arguments = Argument
2015-12-01 19:32:52 +03:00
<$> (flag Split Unified (long "unified" <> help "output a unified diff")
<|> flag' Split (long "split" <> help "output a split diff"))
2015-12-01 19:15:13 +03:00
<*> argument str (metavar "FILE a")
<*> argument str (metavar "FILE b")
2015-11-18 01:44:16 +03:00
main :: IO ()
2015-11-20 19:36:54 +03:00
main = do
2015-12-01 19:19:40 +03:00
arguments <- execParser opts
2015-12-07 22:23:32 +03:00
let (sourceAPath, sourceBPath) = (sourceA arguments, sourceB arguments)
aContents <- readFile sourceAPath
bContents <- readFile sourceBPath
language <- (parserForType . takeExtension) sourceAPath
(aTerm, bTerm) <- case language of
Just lang -> do aTerm <- parseTreeSitterFile lang aContents
bTerm <- parseTreeSitterFile lang bContents
return (aTerm, bTerm)
Nothing -> error ("Unsupported language extension in path: " ++ sourceAPath)
2015-12-07 22:34:58 +03:00
let diff = interpret comparable aTerm bTerm in
case output arguments of
Unified -> do
output <- unified diff aContents bContents
B1.putStr output
Split -> do
output <- split diff aContents bContents
B2.putStr output
where
2015-12-01 19:19:40 +03:00
opts = info (helper <*> arguments)
(fullDesc <> progDesc "Diff some things" <> header "semantic-diff - diff semantically")
2015-11-25 00:51:53 +03:00
2015-12-03 23:10:49 +03:00
parserForType mediaType = sequence $ case mediaType of
".h" -> Just ts_language_c
".c" -> Just ts_language_c
".js" -> Just ts_language_javascript
2015-12-03 23:10:49 +03:00
_ -> Nothing
parseTreeSitterFile :: Ptr TSLanguage -> String -> IO (Term String Info)
parseTreeSitterFile language contents = do
2015-11-24 21:41:30 +03:00
document <- ts_document_make
2015-11-24 21:46:00 +03:00
ts_document_set_language document language
withCString contents (\source -> do
ts_document_set_input_string document source
ts_document_parse document
term <- documentToTerm document contents
2015-11-27 00:25:55 +03:00
ts_document_free document
return term)
2015-11-27 00:22:27 +03:00
documentToTerm :: Ptr TSDocument -> String -> IO (Term String Info)
documentToTerm document contents = alloca $ \root -> do
ts_document_root_node_p document root
snd <$> toTerm root where
toTerm :: Ptr TSNode -> IO (String, Term String Info)
toTerm node = do
name <- ts_node_p_name node document
name <- peekCString name
children <- withNamedChildren node toTerm
range <- range node
2015-12-01 23:04:23 +03:00
lineRange <- getLineRange node
annotation <- return . Info range lineRange $ singleton name
return (name, annotation :< case children of
[] -> Leaf $ substring range contents
2015-11-27 17:19:40 +03:00
_ | member name keyedProductions -> Keyed $ Map.fromList children
_ | member name fixedProductions -> Fixed $ fmap snd children
_ | otherwise -> Indexed $ fmap snd children)
keyedProductions = fromList [ "object" ]
fixedProductions = fromList [ "pair", "rel_op", "math_op", "bool_op", "bitwise_op", "type_op", "math_assignment", "assignment", "subscript_access", "member_access", "new_expression", "function_call", "function", "ternary" ]
2015-11-20 19:36:54 +03:00
withNamedChildren :: Ptr TSNode -> (Ptr TSNode -> IO (String, a)) -> IO [(String, a)]
2015-11-27 00:28:17 +03:00
withNamedChildren node transformNode = do
count <- ts_node_p_named_child_count node
2015-11-26 01:09:42 +03:00
if count == 0
then return []
2015-11-27 00:27:57 +03:00
else mapM (alloca . getChild) [0..pred count] where
getChild n out = do
2015-11-26 01:09:42 +03:00
ts_node_p_named_child node n out
2015-11-27 00:28:17 +03:00
transformNode out
2015-11-25 20:51:11 +03:00
range :: Ptr TSNode -> IO Range
range node = do
pos <- ts_node_p_pos_chars node
size <- ts_node_p_size_chars node
2015-12-02 22:29:19 +03:00
let start = fromIntegral pos
end = start + fromIntegral size
return Range { start = start, end = end }
2015-12-01 23:04:23 +03:00
getLineRange :: Ptr TSNode -> IO Range
getLineRange node = do
startLine <- ts_node_p_start_point node
endLine <- ts_node_p_end_point node
2015-12-02 22:29:19 +03:00
return Range { start = fromIntegral startLine, end = fromIntegral endLine }