packages feed

hscope (empty) → 0.1

raw patch · 14 files changed

+558/−0 lines, 14 filesdep +Unixutilsdep +basedep +bytestringsetup-changed

Dependencies added: Unixutils, base, bytestring, cereal, directory, haskell-src-exts, hs-cdb, mtl, process, test-simple, uniplate, vector

Files

+ COPYING view
@@ -0,0 +1,25 @@+Copyright (c) 2013 Boris Sukholitko+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. The names of the authors may not be used to endorse or promote products+   derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ HScope.hs view
@@ -0,0 +1,194 @@+{-# LANGUAGE BangPatterns #-}+module Main (main) where++import System.Console.GetOpt+import System.Environment (getArgs, getProgName)+import Database.CDB.Write+import Database.CDB.Read+import Database.CDB.Packable+import Language.Haskell.Exts.Annotated+import Control.Monad.Trans (liftIO)+import Control.Monad (void, when)+import System.Process (readProcess)+import Data.List (foldl', intercalate, isInfixOf)+import Data.Maybe+import Data.Serialize+import qualified Data.ByteString.Char8 as B+import qualified Data.Vector as V+import Control.Applicative ((<$>), (<*>))+import System.IO (hFlush, stdout)+import Data.Generics.Uniplate.Data (transformBiM)+import Data.Data++data Flag = Build Bool | File FilePath | Line | Query String | CPPInclude String deriving (Show)++data Config = Config { cBuild :: Bool, cFile :: FilePath, cLine :: Bool+                        , cQuery :: Maybe String, cCPPIncludes :: [String] } deriving Show++data IType = Definition | Call deriving (Enum, Show, Eq)+data Info = Info IType String Int B.ByteString deriving Show+type Lines = V.Vector (Int, B.ByteString)++instance Serialize Info where+    put (Info ity file line bs) = do+        put $ fromEnum ity +        put file+        put line+        put bs++    get = do+        ity <- fmap toEnum get+        Info ity <$> get <*> get <*> get++instance Unpackable Info where+    unpack = either error id . decode++options :: [OptDescr Flag]+options = [+            Option ['b'] [ "build" ] (NoArg $ Build True) "Build hscope database"+            , Option ['d'] [ "dont-update" ] (NoArg $ Build False)+                "Do not update the cross-reference"+            , Option ['l'] [ "line" ] (NoArg $ Line) "Line-oriented interface"+            , Option ['f'] [ "file" ] (ReqArg File "REFFILE") $+                "Use REFFILE as the cross-reference file name instead of the default"+                    ++ " \"hscope.out\""+            , Option ['1'] [ "definition" ] (ReqArg (Query . ('1':)) "SYMBOL") $ "Find the definition of the SYMBOL"+            , Option ['3'] [ "callers" ] (ReqArg (Query . ('3':)) "SYMBOL") $ "Find SYMBOLs calling this SYMBOL"+            , Option ['4'] [ "text" ] (ReqArg (Query . ('4':)) "TEXT") $ "Find TEXT in files"+            , Option ['7'] [ "file" ] (ReqArg (Query . ('7':)) "FILE") $ "Find files matching FILE"+            , Option ['I'] [ "cpp-include" ] (ReqArg CPPInclude "DIRECTORY") $ "Include path for CPP preprocessor"+          ]++parseFlags :: [Flag] -> Config+parseFlags = foldl' go $ Config False "hscope.out" False Nothing [] where+    go c (Build b) = c { cBuild = b }+    go c (File f) = c { cFile = f }+    go c Line = c { cLine = True }+    go c (Query q) = c { cQuery = Just q }+    go c (CPPInclude i) = c { cCPPIncludes = i:(cCPPIncludes c) }++addInfo :: Lines -> IType -> Name SrcSpanInfo -> CDBMake+addInfo vec ity n = cdbAdd f $ encode $ Info ity (fileName src) (fst lp) (snd lp)+    where l = startLine src+          lp = maybe (error $ "Bad line: " ++ show n ++ ", " ++ show vec) id $ vec V.!? (l - 1)+          (src, f) = case n of+            (Ident src' f') -> (src', f')+            (Symbol src' f') -> (src', f')++traverseAST :: (Data b, Monad m, Data a, Functor m) => (a -> m ()) -> b -> m ()+traverseAST cb = void . transformBiM go where go v = cb v >> return v++handleDefinitions :: Lines -> Decl SrcSpanInfo -> CDBMake+handleDefinitions vec = go where+    go (PatBind _ (PVar _ n) _ _ _) = addDef n+    go (FunBind _ ((Match _ n _ _ _):_)) = addDef n+    go _ = return ()+    addDef = addInfo vec Definition++handleCalls :: Lines -> QName SrcSpanInfo -> CDBMake+handleCalls vec (UnQual _ n) = addInfo vec Call n+handleCalls _ _ = return ()++handleConstructors :: Lines -> ConDecl SrcSpanInfo -> CDBMake+handleConstructors vec (ConDecl _ n _) = addInfo vec Definition n+handleConstructors vec (RecDecl _ n recs) = do+    addInfo vec Definition n+    mapM_ go recs+    where go (FieldDecl _ ns _) = mapM_ (addInfo vec Definition) ns+handleConstructors _ c = error $ "handleConstructors " ++ show c++handleDeclarations :: Lines -> DeclHead SrcSpanInfo -> CDBMake+handleDeclarations vec (DHead _ n _) = addInfo vec Definition n+handleDeclarations _ c = error $ "handleDeclarations " ++ show c++mapLines :: Show a => FilePath -> [a] -> [String] -> [a]+mapLines f to = reverse . snd . foldl' go ((to, True), []) where+    go ((xs@(x:_), _), res) ('#':str) = ((xs, isInfixOf f str), x:res)+    go (((x:xs), True), res) _ = ((xs, True), x:res)+    go ((xs@(x:_), False), res) _ = ((xs, False), x:res)+    go t l = error $ "mapLines: " ++ show t ++ " at " ++ l++preprocess :: [String] -> FilePath -> IO (String, Lines)+preprocess idirs f = do+    origs <- B.lines <$> (liftIO $ B.readFile f)+    flns <- fmap lines $ readProcess "cpp" (incs ++ [ "-traditional-cpp", f ]) ""+    return (fconts flns, V.fromList $ mapLines f (zip [1 ..] origs) flns)+    where fconts = intercalate "\n" . map cmnt+          cmnt ('#':_) = ""+          cmnt x = x+          incs = concatMap (\i -> [ "-I", i ]) idirs++parseCurrentFile :: FilePath -> String -> Either String (Module SrcSpanInfo)+parseCurrentFile f fstr = go [] where+    go exts = case parseFileContentsWithMode (pmode exts) fstr of+        ParseOk m -> Right m+        ParseFailed src str -> let (ext, rest) = break (== ' ') str+                in if rest == " is not enabled"+                   then go ((classifyExtension ext):exts)+                   else Left $ str ++ " for " ++ f ++ " at " ++ show src+    pmode exts = defaultParseMode { fixities = Just baseFixities+                            , extensions = exts+                            , parseFilename = f }++buildOne :: Config -> FilePath -> CDBMake+buildOne cfg f = do+    cdbAdd "0_hs_files" f+    (fstr, lns) <- liftIO $ preprocess (cCPPIncludes cfg) f+    either (liftIO . putStrLn) (go lns) $ parseCurrentFile f fstr+    where go lns modul = do+                    traverseAST (handleDefinitions lns) modul+                    traverseAST (handleCalls lns) modul+                    traverseAST (handleConstructors lns) modul+                    traverseAST (handleDeclarations lns) modul+                    -- liftIO $ putStrLn $ show modul++handleQuery :: CDB -> String -> IO ()+handleQuery cdb ('1':str) = findInfo Definition cdb str+handleQuery cdb ('3':str) = findInfo Call cdb str+handleQuery cdb ('4':str) = do+    let files = cdbGetAll cdb "0_hs_files"+    lns <- fmap lines $ readProcess "grep" ("-n":"-H":str:files) ""+    outputLines $ map go lns+    where go l = let (f, rest1) = break (':' ==) l+                     (n, rest2) = break (':' ==) $ drop 1 rest1+                  in f ++ " <unknown> " ++ n ++ " " ++ drop 1 rest2 +handleQuery cdb ('7':str) = do+    let files = cdbGetAll cdb "0_hs_files"+    outputLines $ map go $ filter (isInfixOf str) files+    where go f = f ++ " <unknown> 1 <unknown>"+handleQuery _ _ = return ()++runLines :: CDB -> IO ()+runLines cdb = do+    putStr ">> "+    hFlush stdout+    !l <- getLine+    handleQuery cdb l+    case l of+        ('q':_) -> return ()+        _ -> runLines cdb++outputLines :: [String] -> IO ()+outputLines infos = do+    putStrLn $ "cscope: " ++ (show $ length infos) ++ " lines"+    mapM_ putStrLn infos++findInfo :: IType -> CDB -> String -> IO ()+findInfo ity cdb str = outputLines $ catMaybes $ map go $ cdbGetAll cdb str+    where go (Info t file lno line) | t == ity = Just $ file ++ " " ++ str+                                        ++ " " ++ show lno ++ " " ++ B.unpack line+                                    | otherwise = Nothing++main :: IO ()+main = do+    (flags, rest, _) <- fmap (getOpt Permute options) getArgs+    let cfg = parseFlags flags+    when (cBuild cfg) $ cdbMake (cFile cfg) $ mapM_ (buildOne cfg) rest+    if null flags then usage else go cfg+    where usage = do+            pn <- getProgName+            putStrLn $ usageInfo ("Usage: " ++ pn ++ " [OPTION...] files...") options+          go cfg = do+            cdb <- cdbInit $ cFile cfg+            maybe (return ()) (handleQuery cdb) $ cQuery cfg+            when (cLine cfg) $ runLines cdb
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hscope.cabal view
@@ -0,0 +1,50 @@+Name:           hscope+Version:        0.1+License:        BSD3+License-File:   COPYING+Category:       source-tools+Synopsis:       cscope like browser for Haskell code+Description:+    hscope is a partial cscope line oriented mode reimplementation for Haskell code.++    Currently it supports finding the definition and callers of the function, types.+    Also works searching for files and in files.+    +    See hscope --help for available options.++    Being cscope-compatible gives Vim integration for free. Use :set cscopeprg+    to configure hscope as cscope replacement for haskell files.++Cabal-Version:  >= 1.10+Build-Type:     Simple+Author:         Boris Sukholitko <boriss@gmail.com>+Copyright:      Boris Sukholitko <boriss@gmail.com>+Maintainer:     Boris Sukholitko <boriss@gmail.com>+homepage:       https://github.com/bosu/hscope+Stability:      Experimental+Extra-Source-Files:+    t/files/Arrow.hs+    t/files/a/c.h+    t/files/a/a.h+    t/files/b/b.h+    t/files/CPP2.hs+    t/files/garrs.hs+    t/files/Simple.hs+    t/files/CPP.hs+    t/files/a.c++Executable hscope+    Main-Is: HScope.hs+    GHC-Options: -Wall+    Build-Depends: base (< 5), hs-cdb, haskell-src-exts, mtl, uniplate, cereal+                             , vector, bytestring, process+    Default-Language: Haskell2010++test-suite Build+    type:            exitcode-stdio-1.0+    build-depends:   base, test-simple, process, directory, Unixutils, mtl+    ghc-options:     -Wall+    hs-source-dirs:  t+    main-is:         Build.hs+    Default-Language: Haskell2010+
+ t/Build.hs view
@@ -0,0 +1,129 @@+{-# OPTIONS -fno-warn-unused-do-bind -XBangPatterns #-}+module Main (main) where++import Test.Simple+import System.Unix.Directory (withTemporaryDirectory)+import System.Directory (canonicalizePath, doesFileExist)+import System.Process (system, readProcess, runInteractiveProcess, waitForProcess)+import System.Exit (ExitCode(ExitSuccess))+import Control.Monad.Trans (liftIO)+import System.IO (hGetContents, hPutStrLn, hFlush, hSetBinaryMode)++hscopeInteract :: FilePath -> FilePath -> [String] -> IO (ExitCode, String)+hscopeInteract hpath td cmds = do+    (inp, out, _, ph1) <- runInteractiveProcess hpath [ "-dl", "-f", td ++ "/hscope.out" ]+                                Nothing Nothing+    hSetBinaryMode inp False+    mapM_ (go inp) $ cmds ++ [ "q" ]++    !l1 <- hGetContents out+    ec2 <- waitForProcess ph1+    return (ec2, l1)+    where go inp cmd = hPutStrLn inp cmd >> hFlush inp++main :: IO ()+main = withTemporaryDirectory "/tmp/hscope_test_XXXXXX" $ \td -> testSimpleMain $ do+    plan 42+    hpath <- liftIO $ canonicalizePath "./dist/build/hscope/hscope"+    tfile <- liftIO $ canonicalizePath "./t/files/Simple.hs"+    ec1 <- liftIO $ system $ "cd " ++ td ++ " && " ++ hpath ++ " -b " ++ tfile+    res1 <- liftIO $ readProcess "cdb" [ "-l", td ++ "/hscope.out" ] ""+    is ec1 ExitSuccess+    like res1 "main"++    res2 <- liftIO $ readProcess "cdb" [ "-q", td ++ "/hscope.out", "main" ] ""+    like res2 "main = "++    res3 <- liftIO $ readProcess "cdb" [ "-q", td ++ "/hscope.out", "findInfo" ] ""+    like res3 "-> findInfo "++    res4 <- liftIO $ readProcess "cdb" [ "-q", td ++ "/hscope.out", "options" ] ""+    like res4 "Permute options"++    res5 <- liftIO $ readProcess "cdb" [ "-q", td ++ "/hscope.out", "runLines" ] ""+    like res5 ">>= runLines"++    (ec2, l1) <- liftIO $ hscopeInteract hpath td [ "1main", "1findInfo" ]+    is ec2 ExitSuccess+    like l1 ">> "+    like l1 tfile+    like l1 "main ="+    like l1 "findInfo ity cdb str ="++    (ec3, l2) <- liftIO $ hscopeInteract hpath td [ "3findInfo", "1Build", "1Config" ]+    is ec3 ExitSuccess+    like l2 "-> findInfo "+    like l2 "2 lines"+    like l2 "Build Bool"+    like l2 "    Config {"++    (ec4, l3) <- liftIO $ hscopeInteract hpath td [ "1Flag", "1Unused", "1cBuild" ]+    is ec4 ExitSuccess+    like l3 "data Flag"+    like l3 "type Unused"+    like l3 "cBuild :: Bool"++    (ec5, l4) <- liftIO $ hscopeInteract hpath td [ "3cBuild", "3Flag", "3==" ]+    is ec5 ExitSuccess+    like l4 "cBuild cfg"+    like l4 "parseFlags ::"+    like l4 "groupBy (==)"++    res6 <- liftIO $ readProcess hpath [ "-b", "-f", td ++ "/foo.out", "t/files/a.c" ] ""+    like res6 "Parse error: 0"+    like res6 "a.c"++    dfoo <- liftIO $ doesFileExist $ td ++ "/foo.out"+    is dfoo True++    res7 <- liftIO $ readProcess hpath [ "-b", "-f", td ++ "/foo.out"+                                            , "t/files/Arrow.hs", "t/files/CPP.hs" ] ""+    is res7 ""++    res8 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/foo.out"+                                            , "-1", "doSth" ] ""+    like res8 "doSth ="++    res9 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/foo.out"+                                            , "-1", "foo" ] ""+    like res9 "foo 6 bar = id"+    unlike res9 "warning"++    res9_1 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/foo.out"+                                            , "-7", "Ar" ] ""+    is res9_1 "cscope: 1 lines\nt/files/Arrow.hs <unknown> 1 <unknown>\n"++    res10 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/hscope.out"+                                            , "-3", "groupBy" ] ""+    like res10 "groupBy (==)"++    res10_1 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/hscope.out"+                                            , "-3", "Build" ] ""+    like res10_1 "NoArg $ Build False"+    like res10_1 "Build b) ="++    res11 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/hscope.out"+                                            , "-4", "= do" ] ""+    like res11 "files/Simple.hs <unknown> 115 main = do"++    res12 <- liftIO $ readProcess hpath [ "-b", "-f", td ++ "/foo.out"+                                    , "-I", "t/files/a", "-I", "t/files/b"+                                    , "t/files/CPP2.hs" ] ""+    is res12 ""++    res13 <- liftIO $ readProcess hpath [ "-d", "-f", td ++ "/foo.out"+                                            , "-1", "bar" ] ""+    like res13 "bar 7 bar = id"++    res14 <- liftIO $ readProcess hpath [ "-b", "-f", td ++ "/foo.out"+                                    , "t/files/garrs.hs" ] ""+    is res14 ""++    res15 <- liftIO $ readProcess hpath [ "--help" ] ""+    like res15 $ "Usage: hscope [OPTION...] files..."+    like res15 "-b"++    res16 <- liftIO $ readProcess hpath [] ""+    is res16 res15++    return ()
+ t/files/Arrow.hs view
@@ -0,0 +1,6 @@+module Main+where++import Control.Arrow++doSth = (+ 1) . head &&& tail
+ t/files/CPP.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE CPP #-}++#define bar foo++bar :: Int -> Int+bar = id++baz' = 12
+ t/files/CPP2.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE CPP #-}++#include "a.h"+#include "b.h"++bar :: Int -> Int+bar = id
+ t/files/Simple.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE BangPatterns #-}+module Main (main) where++import System.Console.GetOpt+import System.Environment (getArgs)+import Database.CDB.Write+import Database.CDB.Read+import Database.CDB.Packable+import Language.Haskell.Exts (parseFile)+import Language.Haskell.Exts.Parser (ParseResult(ParseOk))+import Control.Monad.Trans (liftIO)+import Control.Monad (when)+import Data.List (foldl')+import Data.Maybe+import Data.IORef+import Data.Generics.Zipper.Bubble (bubble, Callback(..))+import Language.Haskell.Exts.Syntax (Decl(PatBind, FunBind), Name(Ident)+                        , Pat(PVar), SrcLoc(..), Match(Match), Exp(..), QName(UnQual))+import Data.Serialize+import qualified Data.ByteString.Char8 as B+import qualified Data.Vector as V+import Control.Applicative ((<$>), (<*>))+import System.IO (hFlush, stdout)++type Unused = Int++testSymbol :: Eq a => [a] -> [a]+testSymbol = groupBy (==)++data Flag = Build Bool | File FilePath | Line deriving (Show)++data Config =+    Config { cBuild :: Bool, cFile :: FilePath, cLine :: Bool } deriving Show++data IType = Definition | Call deriving (Enum, Show, Eq)+data Info = Info IType String Int B.ByteString deriving Show++instance Serialize Info where+    put (Info ity file line bs) = do+        put $ fromEnum ity +        put file+        put line+        put bs++    get = do+        ity <- fmap toEnum get+        Info ity <$> get <*> get <*> get++instance Unpackable Info where+    unpack = either error id . decode++options :: [OptDescr Flag]+options = [+            Option ['b'] [ "build" ] (NoArg $ Build True) "Build hscope database"+            , Option ['d'] [ "dont-update" ] (NoArg $ Build False) "Do not update the cross-reference"+            , Option ['l'] [ "line" ] (NoArg $ Line) "Line-oriented interface"+            , Option ['f'] [ "file" ] (ReqArg File "REFFILE") $+                "Use REFFILE as the cross-reference file name instead of the default \"hscope.out\""+          ]++parseFlags :: [Flag] -> Config+parseFlags = foldl' go $ Config False "hscope.out" False where+    go c (Build b) = c { cBuild = b }+    go c (File f) = c { cFile = f }+    go c Line = c { cLine = True }++addInfo :: V.Vector B.ByteString -> SrcLoc -> IType -> String -> CDBMake+addInfo vec (SrcLoc file line _) ity f = cdbAdd f $ encode $ Info ity file line (vec V.! (line - 1))++handleDefinitions :: V.Vector B.ByteString -> Decl -> CDBMake+handleDefinitions vec = go where+    go (PatBind src (PVar (Ident f)) _ _ _) = addDef f src+    go (FunBind ((Match src (Ident f) _ _ _ _):_)) = addDef f src+    go _ = return ()+    addDef f src = addInfo vec src Definition f++handleSrcLoc :: IORef SrcLoc -> SrcLoc -> CDBMake+handleSrcLoc ior = liftIO . writeIORef ior++handleCalls :: IORef SrcLoc -> V.Vector B.ByteString -> Exp -> CDBMake+handleCalls ior vec (Var (UnQual (Ident f))) = do+    src <- liftIO $ readIORef ior+    addInfo vec src Call f+handleCalls _ _ _ = return ()++buildOne :: FilePath -> CDBMake+buildOne f = do+    ParseOk modul <- liftIO $ parseFile f+    lns <- V.fromList <$> B.lines <$> (liftIO $ B.readFile f)+    sior <- liftIO $ newIORef $ SrcLoc f 1 1+    bubble [Callback $ handleSrcLoc sior, Callback $ handleDefinitions lns, Callback $ handleCalls sior lns] [] modul++runLines :: CDB -> IO ()+runLines cdb = do+    putStr ">> "+    hFlush stdout+    !l <- getLine+    case l of+        ('q':_) -> return ()+        ('1':str) -> findInfo Definition cdb str+        ('3':str) -> findInfo Call cdb str+        _ -> runLines cdb++findInfo :: IType -> CDB -> String -> IO ()+findInfo ity cdb str = do+    let infos = catMaybes $ map go $ cdbGetAll cdb str+    putStrLn $ "cscope: " ++ (show $ length infos) ++ " lines"+    mapM_ putStrLn infos+    runLines cdb+    where go (Info t file lno line) | t == ity = Just $ file ++ " " ++ str+                                        ++ " " ++ show lno ++ " " ++ B.unpack line+                                    | otherwise = Nothing++main :: IO ()+main = do+    (flags, rest, _) <- fmap (getOpt Permute options) getArgs+    let cfg = parseFlags flags+    when (cBuild cfg) $ cdbMake "hscope.out" $ mapM_ buildOne rest+    when (cLine cfg) $ (cdbInit $ cFile cfg) >>= runLines
+ t/files/a.c view
@@ -0,0 +1,7 @@+static int foo() {+	return 0;+}++int main() {+	return foo();+}
+ t/files/a/a.h view
@@ -0,0 +1,1 @@+#include "c.h"
+ t/files/a/c.h view
@@ -0,0 +1,3 @@+type Hello = Int++type Bye = Bool
+ t/files/b/b.h view
@@ -0,0 +1,1 @@+data Foo = Foo
+ t/files/garrs.hs view
@@ -0,0 +1,6 @@+data G a where+    A :: a -> G Int+    B :: b -> G Bool++g :: Int -> Int+g proc = proc + 1