summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2018-03-13 16:33:54 -0400
committerjoe <joe@jerkface.net>2018-03-13 16:33:54 -0400
commit0f917b68b3a81bb4d79bc2954a01588558f12f63 (patch)
tree4c1d151aed360972988614217d1bd99955dc7812
parent3d3327ffa257b7b58e6d5c4b83a13d55071720a4 (diff)
NetSurf efforts
-rw-r--r--c2haskell.hs453
1 files changed, 294 insertions, 159 deletions
diff --git a/c2haskell.hs b/c2haskell.hs
index 8397f98..03ba9cd 100644
--- a/c2haskell.hs
+++ b/c2haskell.hs
@@ -1,169 +1,304 @@
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE NondecreasingIndentation #-}
5{-# LANGUAGE QuasiQuotes #-}
6{-# LANGUAGE TemplateHaskell #-}
1 7
2import Language.C 8import Data.Generics.Aliases
9import Data.Generics.Schemes
10import Debug.Trace
11import Control.Monad
12import Data.Char
13import Data.Data
14import Data.List
15import qualified Data.Map as Map
16 ;import Data.Map (Map)
17import Data.Maybe
18import qualified Data.Set as Set
19 ;import Data.Set (Set)
20import Language.C.Data.Ident as C
21import Language.C as C hiding (prettyUsingInclude)
3import Language.C.System.GCC 22import Language.C.System.GCC
23import Language.Haskell.Exts.Pretty as HS
24import Language.Haskell.Exts.Syntax as HS
25import Language.Haskell.TH
26import Language.Haskell.TH.Ppr
27import Language.Haskell.TH.Syntax as TH
4import System.Environment 28import System.Environment
5import System.IO 29import System.IO
30import Text.PrettyPrint (Doc, doubleQuotes, empty, text, vcat, ($$),
31 (<+>))
32import Text.Show.Pretty
6 33
7hack_incs = [ "-I." ] 34-- | Pretty print the given tranlation unit, but replace declarations from header files with @#include@ directives.
8 35--
9-- pkg-config --cflags clutter-1.0 36-- The resulting file may not compile (because of missing @#define@ directives and similar things), but is very useful
10clutter_incs = 37-- for testing, as otherwise the pretty printed file will be cluttered with declarations from system headers.
11 [ "-pthread" 38prettyUsingInclude :: CTranslUnit -> Doc
12 , "-I/usr/include/clutter-1.0" 39prettyUsingInclude (CTranslUnit edecls _) =
13 , "-I/usr/include/cogl" 40 includeWarning headerFiles
14 , "-I/usr/include/atk-1.0" 41 $$
15 , "-I/usr/include/cogl" 42 vcat (map (either includeHeader pretty) mappedDecls)
16 , "-I/usr/include/libdrm" 43 where
17 , "-I/usr/include/json-glib-1.0" 44 (headerFiles,mappedDecls) = foldr (addDecl . tagIncludedDecls) (Set.empty,[]) edecls
18 , "-I/usr/include/gtk-3.0" 45 tagIncludedDecls edecl | maybe False isHeaderFile (fileOfNode edecl) = Left ((posFile . posOf) edecl)
19 , "-I/usr/include/gio-unix-2.0/" 46 | otherwise = Right edecl
20 , "-I/usr/include/cairo" 47 addDecl decl@(Left headerRef) (headerSet, ds)
21 , "-I/usr/include/pango-1.0" 48 | Set.member headerRef headerSet = (headerSet, ds)
22 , "-I/usr/include/harfbuzz" 49 | otherwise = (Set.insert headerRef headerSet, decl : ds)
23 , "-I/usr/include/pango-1.0" 50 addDecl decl (headerSet,ds) = (headerSet, decl : ds)
24 , "-I/usr/include/gdk-pixbuf-2.0" 51 includeHeader hFile = text "#include" <+> doubleQuotes (text hFile)
25 , "-I/usr/include/libpng16" 52 isHeaderFile = (".h" `isSuffixOf`)
26 , "-I/usr/include/cairo" 53 includeWarning hs | Set.null hs = empty
27 , "-I/usr/include/pixman-1" 54 | otherwise = text "/* Warning: The #include directives in this file aren't necessarily correct. */"
28 , "-I/usr/include/freetype2"
29 , "-I/usr/include/libpng16"
30 , "-I/usr/include/glib-2.0"
31 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
32 ]
33
34
35-- pkg-config --cflags cogl-pango-2.0-experimental
36cogl_pango2_incs =
37 [ "-D COGL_ENABLE_EXPERIMENTAL_2_0_API"
38 , "-pthread"
39 , "-I/usr/include/cogl"
40 , "-I/usr/include/cairo"
41 , "-I/usr/include/pixman-1"
42 , "-I/usr/include/freetype2"
43 , "-I/usr/include/libpng16"
44 , "-I/usr/include/gdk-pixbuf-2.0"
45 , "-I/usr/include/libpng16"
46 , "-I/usr/include/glib-2.0"
47 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
48 , "-I/usr/include/libdrm"
49 ]
50
51
52-- pkg-config cogl-pango-1.0 --cflags
53cogl_pango_incs =
54 [ "-pthread"
55 , "-I/usr/include/cogl"
56 , "-I/usr/include/cairo"
57 , "-I/usr/include/pixman-1"
58 , "-I/usr/include/freetype2"
59 , "-I/usr/include/libpng16"
60 , "-I/usr/include/gdk-pixbuf-2.0"
61 , "-I/usr/include/libpng16"
62 , "-I/usr/include/glib-2.0"
63 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
64 , "-I/usr/include/libdrm" ]
65
66-- pkg-config gtk+-3.0 --cflags
67gtk_incs =
68 [ "-pthread"
69 , "-I/usr/include/gtk-3.0"
70 , "-I/usr/include/at-spi2-atk/2.0"
71 , "-I/usr/include/at-spi-2.0"
72 , "-I/usr/include/dbus-1.0"
73 , "-I/usr/lib/x86_64-linux-gnu/dbus-1.0/include"
74 , "-I/usr/include/gtk-3.0"
75 , "-I/usr/include/gio-unix-2.0/"
76 , "-I/usr/include/cairo"
77 , "-I/usr/include/pango-1.0"
78 , "-I/usr/include/harfbuzz"
79 , "-I/usr/include/pango-1.0"
80 , "-I/usr/include/atk-1.0"
81 , "-I/usr/include/cairo"
82 , "-I/usr/include/pixman-1"
83 , "-I/usr/include/freetype2"
84 , "-I/usr/include/libpng16"
85 , "-I/usr/include/gdk-pixbuf-2.0"
86 , "-I/usr/include/libpng16"
87 , "-I/usr/include/glib-2.0"
88 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
89 ]
90
91-- pkg-config --cflags gobject-introspection-1.0
92gobj_incs =
93 [ "-pthread"
94 , "-I/usr/include/gobject-introspection-1.0"
95 , "-I/usr/include/glib-2.0"
96 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
97 ]
98
99-- pkg-config --cflags libmutter-1
100mutter_incs =
101 [ "-pthread"
102 , "-I/usr/include/mutter"
103 , "-I/usr/include/gtk-3.0"
104 , "-I/usr/include/at-spi2-atk/2.0"
105 , "-I/usr/include/at-spi-2.0"
106 , "-I/usr/include/dbus-1.0"
107 , "-I/usr/lib/x86_64-linux-gnu/dbus-1.0/include"
108 , "-I/usr/include/gtk-3.0"
109 , "-I/usr/include/gio-unix-2.0/"
110 , "-I/usr/include/cairo"
111 , "-I/usr/include/mutter/clutter-1"
112 , "-I/usr/include/cairo"
113 , "-I/usr/include/atk-1.0"
114 , "-I/usr/include/pango-1.0"
115 , "-I/usr/include/harfbuzz"
116 , "-I/usr/include/pango-1.0"
117 , "-I/usr/include/json-glib-1.0"
118 , "-I/usr/include/mutter/cogl"
119 , "-I/usr/include/cairo"
120 , "-I/usr/include/pixman-1"
121 , "-I/usr/include/freetype2"
122 , "-I/usr/include/libpng16"
123 , "-I/usr/include/gdk-pixbuf-2.0"
124 , "-I/usr/include/libpng16"
125 , "-I/usr/include/glib-2.0"
126 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
127 , "-I/usr/include/libdrm"
128 , "-I/usr/include/gsettings-desktop-schemas"
129 ]
130
131-- pkg-config --cflags gjs-1.0
132gjs_incs =
133 [ "-pthread"
134 , "-include"
135 , "/usr/include/mozjs-52/js/RequiredDefines.h"
136 , "-pthread"
137 , "-I/usr/include/gobject-introspection-1.0"
138 , "-I/usr/include/mozjs-52"
139 , "-I/usr/include/gtk-3.0"
140 , "-I/usr/include/at-spi2-atk/2.0"
141 , "-I/usr/include/at-spi-2.0"
142 , "-I/usr/include/dbus-1.0"
143 , "-I/usr/lib/x86_64-linux-gnu/dbus-1.0/include"
144 , "-I/usr/include/gtk-3.0"
145 , "-I/usr/include/gio-unix-2.0/"
146 , "-I/usr/include/cairo"
147 , "-I/usr/include/pango-1.0"
148 , "-I/usr/include/harfbuzz"
149 , "-I/usr/include/pango-1.0"
150 , "-I/usr/include/atk-1.0"
151 , "-I/usr/include/cairo"
152 , "-I/usr/include/pixman-1"
153 , "-I/usr/include/freetype2"
154 , "-I/usr/include/libpng16"
155 , "-I/usr/include/gdk-pixbuf-2.0"
156 , "-I/usr/include/libpng16"
157 , "-I/usr/include/glib-2.0"
158 , "-I/usr/lib/x86_64-linux-gnu/glib-2.0/include"
159 , "-I/usr/include/gjs-1.0"
160 ]
161 55
56specs :: CExternalDeclaration a -> [CDeclarationSpecifier a]
57specs (CFDefExt (CFunDef specs _ _ _ _)) = specs
58specs (CDeclExt (CDecl specs _ _)) = specs
59specs _ = []
162 60
61declrSym :: CDeclarator t -> Maybe Ident
62declrSym (CDeclr m _ _ _ _) = m
63
64sym :: CExternalDeclaration a -> [Maybe Ident]
65sym (CFDefExt (CFunDef specs m _ _ _)) = [ declrSym m ]
66sym (CDeclExt (CDecl specs ms _)) = ms >>= \(m,_,_) -> maybe [] (pure . declrSym) m
67sym _ = []
68
69isStatic :: CDeclarationSpecifier a -> Bool
70isStatic (CStorageSpec (CStatic _)) = True
71isStatic _ = False
72
73capitalize :: String -> String
74capitalize xs = concatMap (cap . drop 1) gs
75 where
76 gs = groupBy (\a b -> b/='_') $ '_':xs
77 cap (c:cs) = toUpper c : cs
78
79transField :: CDeclaration t -> [(TH.Name, TH.Bang, TH.Type)]
80transField (CDecl [CTypeSpec (CTypeDef ctyp _)] vars _)
81 = do
82 let typname = mkName . capitalize . identToString $ ctyp
83 (var,Nothing,Nothing) <- vars
84 CDeclr (Just fident) ptrdeclr Nothing [] _ <- maybeToList var
85 let fieldName = mkName $ identToString fident
86 ftyp = case ptrdeclr of
87 [] -> ConT typname
88 [CPtrDeclr [] _] -> AppT (ConT (mkName "Ptr")) (ConT typname)
89 [ (fieldName, Bang NoSourceUnpackedness NoSourceStrictness, ftyp) ]
90transField (CDecl [CTypeSpec (CSUType (CStruct CStructTag mctyp Nothing [] _) _)] vars _)
91 | Just typname <- mkName . capitalize . identToString <$> mctyp
92 = do
93 (var,Nothing,Nothing) <- vars
94 CDeclr (Just fident) ptrdeclr Nothing [] _ <- maybeToList var
95 let fieldName = mkName $ identToString fident
96 ftyp = case ptrdeclr of
97 [] -> ConT typname
98 [CPtrDeclr [] _] -> AppT (ConT (mkName "Ptr")) (ConT typname)
99 [ (fieldName, Bang NoSourceUnpackedness NoSourceStrictness, ftyp) ]
100
101
102transField _ = []
103
104transpile (CDeclExt (CDecl [ CTypeSpec (CSUType
105 (CStruct CStructTag mbIdent (Just fields) [] _)
106 _) ]
107 []
108 _) )
109 | Just struct_name <- capitalize . identToString <$> mbIdent
110 , let typ = mkName struct_name
111 = Just $ returnQ $ DataD [] typ [] Nothing [RecC typ fs] []
112 where fs = fields >>= transField
113
114transpile _ = Nothing
115
116
117isHeaderDecl :: CNode a => a -> Bool
118isHeaderDecl = maybe False (isSuffixOf ".h") . fileOfNode
119
120-- bar :: CExternalDeclaration NodeInfo -> ()
121-- bar (CDeclExt (CDecl xs [] (NodeInfo pos poslen name))) = ()
122
123data SymbolInformation c = SymbolInformation
124 { symbolLocal :: Bool
125 , symbolStatic :: Bool
126 , symbolSource :: c
127 }
128 deriving (Eq,Ord,Show)
129
130symbolInformation = SymbolInformation
131 { symbolLocal = False
132 , symbolStatic = False
133 , symbolSource = mempty
134 }
135
136data Transpile c = Transpile
137 { syms :: Map String (SymbolInformation c)
138 }
139
140initTranspile = Transpile
141 { syms = Map.empty
142 }
143
144-- grokSymbol :: CExternalDeclaration a -> String -> Maybe SymbolInformation -> Maybe SymbolInformation
145grokSymbol d k msi =
146 let si = fromMaybe symbolInformation msi
147 in Just $ si
148 { symbolLocal = symbolLocal si || not (isHeaderDecl d)
149 , symbolStatic = symbolStatic si || any isStatic (specs d)
150 , symbolSource = d : symbolSource si
151 }
152
153-- update :: CExternalDeclaration a -> Transpile -> Transpile
154update d transpile = transpile
155 { syms = foldr (\k m -> Map.alter (grokSymbol d k) k m) (syms transpile)
156 $ map (maybe "" identToString) $ sym d
157 }
158
159data FunctionSignature t = FunctionSignature
160 { funReturnType :: t
161 , funArgTypes :: [t]
162 }
163
164hsMkName :: String -> HS.QName ()
165hsMkName str = HS.UnQual () (foo () str)
166 where
167 foo = HS.Ident -- alternative: HS.Symbol
168
169hsTypeSpec :: CDeclarationSpecifier t -> [String]
170hsTypeSpec (CTypeSpec (CTypeDef ctyp _)) = [capitalize . identToString $ ctyp]
171hsTypeSpec (CTypeSpec (CBoolType _)) = ["Bool"]
172hsTypeSpec (CTypeSpec (CSUType (CStruct CStructTag mctyp Nothing [] _) _)) = maybeToList $ fmap (capitalize . identToString) mctyp
173hsTypeSpec _ = []
174
175-- hsTransField :: [CDeclarationSpecifier t3] -> [(Maybe (CDeclarator t2), Maybe t1, Maybe t)] -> [HS.Decl ()]
176hsTransField ctyps vars
177 = do
178 typname <- hsMkName <$> (hsTypeSpec =<< ctyps)
179 (var,Nothing,Nothing) <- vars
180 CDeclr (Just fident) ptrdeclr Nothing [] _ <- maybeToList var
181 let btyp = HS.TyCon () typname
182 grok bs b = case bs of
183 [] -> b
184 (CPtrDeclr [] _:cs) -> HS.TyApp () (HS.TyCon () (hsMkName "Ptr")) (grok cs b)
185 [CFunDeclr (Right (args,flg)) attrs _] -> let (as,ts) = unzip $ concatMap (\(CDecl rs as _) -> hsTransField rs as) args
186 in foldr (HS.TyFun ()) b ts
187 _ -> HS.TyCon () (hsMkName $ show $ map (fmap (const ())) ptrdeclr)
188 ftyp = grok ptrdeclr btyp
189 fieldName = identToString fident
190 [ ( fieldName, ftyp ) ]
191{-
192transField (CDecl [CTypeSpec (CSUType (CStruct CStructTag mctyp Nothing [] _) _)] vars _)
193 | Just typname <- mkName . capitalize . identToString <$> mctyp
194 = do
195 (var,Nothing,Nothing) <- vars
196 CDeclr (Just fident) ptrdeclr Nothing [] _ <- maybeToList var
197 let fieldName = mkName $ identToString fident
198 ftyp = case ptrdeclr of
199 [] -> ConT typname
200 [CPtrDeclr [] _] -> AppT (ConT (mkName "Ptr")) (ConT typname)
201 [ (fieldName, Bang NoSourceUnpackedness NoSourceStrictness, ftyp) ]
202hsTransField _ _ = []
203-}
204
205hsTransFieldExt rs as = concatMap (\(fieldName,ftyp)-> [ HS.TypeSig () [ HS.Ident () fieldName ] ftyp ])
206 $ hsTransField rs as
207
208sig (CDeclExt (CDecl rs as _)) = hsTransFieldExt rs as
209sig (CFDefExt (CFunDef rs cdeclr [] bdy _)) = hsTransFieldExt rs [(Just cdeclr, Nothing, Nothing)]
210
211body0 (CFDefExt (CFunDef rs cdeclr [] bdy _)) = Just bdy
212body0 _ = Nothing
213
214body (CFDefExt (CFunDef rs cdeclr [] (CCompound [] bdy _) _)) = bdy
215body _ = []
216
217data SideEffect = PointerWrite | FunctionCall
218
219calls :: Data t => t -> [CExpression NodeInfo]
220calls = everything (++) (mkQ [] (\case { cc@C.CCall {} -> [cc] ; _ -> [] }))
221
222mutations1 e@(CAssign {}) = [e]
223mutations1 e@(CUnary CPreIncOp _ _) = [e]
224mutations1 e@(CUnary CPreDecOp _ _) = [e]
225mutations1 e@(CUnary CPostIncOp _ _) = [e]
226mutations1 e@(CUnary CPostDecOp _ _) = [e]
227mutations1 _ = []
228
229mutations :: Data t => t -> [CExpression NodeInfo]
230mutations = everything (++) (mkQ [] mutations1)
231
232
233-- gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> a -> c a
234--
235-- gfoldl app con
236--
237-- does is to turn such a value into
238--
239-- con C `app` x_1 `app` x_2 ... `app` x_n
240
241
242commented :: String -> String
243commented s = unlines $ map ("-- " ++) (lines s)
244
245c2haskell cs (CTranslUnit edecls _) = do
246 let db = foldr update initTranspile edecls
247 es = Map.filter (\d -> symbolLocal d && not (symbolStatic d)) (syms db)
248 forM_ (Map.toList es) $ \(k,si) -> do
249 putStrLn ""
250 putStrLn (commented k)
251 forM_ (symbolSource si) $ \d -> do
252 putStr $ commented (ppShow (fmap (const ()) d))
253 putStr $ commented (show $ pretty d)
254 mapM_ (putStrLn . HS.prettyPrint) (sig d)
255 {-
256 forM_ (body d) $ \stmt -> do
257 putStr $ commented (take 130 $ show (fmap (const ()) stmt))
258 putStr $ commented (ppShow (fmap (const ()) stmt))
259 putStrLn $ commented . show . pretty $ stmt
260 putStr $ commented "calls"
261 mapM_ (putStr . commented . show . pretty) (calls (body d))
262 putStrLn "--"
263 putStr $ commented "mutations"
264 mapM_ (putStr . commented . show . pretty) (mutations (body d))
265 -}
266
267readComments fname = parseComments 1 1 <$> readFile fname
268
269findCloser !1 (l,c,b) ('*':'/':_) = (l,c+2,b+2)
270findCloser !d (l,c,b) ('*':'/':xs) = findCloser (d - 1) (l,c+2,b+2) xs
271findCloser !d (l,c,b) ('/':'*':xs) = findCloser (d + 1) (l,c+2,b+2) xs
272findCloser !d (l,c,b) ('\n':xs) = findCloser d (l+1,1,b+1) xs
273findCloser !d (l,c,b) (_:xs) = findCloser d (l,c+1,b+1) xs
274findCloser !d (l,c,b) [] = (l,c,b)
275
276mkComment lin no str = (lin,no,str)
277
278parseComments !lin !col = \case
279 ('/':'*':cs) -> let (lcnt,col',bcnt) = findCloser 1 (0,col,0) cs
280 (xs,cs') = splitAt bcnt cs
281 in mkComment lin col xs : parseComments (lin + lcnt) col' cs'
282 ('/':'/':cs) -> let (comment,ds) = break (=='\n') cs
283 in mkComment lin col comment : parseComments (lin + 1) 1 cs
284 ('\n' : cs) -> parseComments (lin+1) 1 cs
285 ( x : cs) -> parseComments lin (col+1) cs
286 [] -> []
287
288sanitizeArgs :: [String] -> [String]
289sanitizeArgs (('-':'M':_):args) = sanitizeArgs args
290sanitizeArgs (('-':'O':_):args) = sanitizeArgs args
291sanitizeArgs (('-':'c':_):args) = sanitizeArgs args
292sanitizeArgs ("-o":args) = sanitizeArgs $ drop 1 args
293sanitizeArgs (arg:args) = arg : sanitizeArgs args
294sanitizeArgs [] = []
295
296main :: IO ()
163main = do 297main = do
164 args <- getArgs 298 args <- getArgs
165 let fname = last args 299 let fname = last args
166 opts = ["-DGETTEXT_PACKAGE=c2haskell"] ++ init args 300 opts = init args
167 incs = "-I." : gjs_incs ++ gobj_incs ++ mutter_incs ++ clutter_incs ++ cogl_pango2_incs ++ gtk_incs 301 r <- parseCFile (newGCC "gcc") Nothing (sanitizeArgs opts) fname
168 r <- parseCFile (newGCC "gcc") Nothing (incs ++ opts) fname 302 -- print (fmap prettyUsingInclude r)
169 print (fmap prettyUsingInclude r) 303 cs <- readComments fname
304 either print (c2haskell cs) r