-
Notifications
You must be signed in to change notification settings - Fork 11
/
proto2hs.hs
executable file
·112 lines (90 loc) · 2.69 KB
/
proto2hs.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Prelude hiding (
readFile, putStr, putStrLn, takeWhile, hPutStrLn, writeFile,
unlines, unwords)
import Data.Attoparsec.Text
import Data.Text.IO
import Data.Text (unwords, unlines, pack, unpack)
import System.Exit
import System.IO (stderr)
import Data.Maybe
import Control.Applicative
import Data.Monoid
import Data.List (intersperse)
import Data.Char
import Control.Monad
import Debug.Trace
main = do
proto <- readFile "ql2.proto"
case parseOnly protoFile proto of
Left err -> hPutStrLn stderr ("Error: " <> pack err) >> exitWith ExitSuccess
Right mod -> do
writeFile "Database/RethinkDB/Wire.hs" genRaw
forM_ mod $ \(name, enums) ->
maybe (return ()) (writeFile (unpack $ "Database/RethinkDB/Wire/" <> name <> ".hs"))
(renderMessage (name, enums))
protoFile = tr "protoFile" $ do
many message
message = tr "message" $ do
token "message"
n <- name
token "{"
body <- catMaybes <$> many justEnums
token "}"
return (n, body)
justEnums = tr "justEnums" $ choice [
Just <$> enum,
const Nothing <$> field,
const Nothing <$> message
]
field = tr "field" $ do
choice [token "repeated", token "optional", token "extensions"]
skipWhile (/=';')
string ";"
enum = tr "enum" $ do
token "enum"
n <- name
token "{"
d <- many decl
token "}"
return (n,d)
decl = tr "decl" $ do
n <- name
token "="
v <- value
choice [token ";", string ";"]
return (n,v)
value = tr "value" $ whitespace >> takeWhile (\c -> not (isSpace c) && c /= ';')
name = tr "name" $ whitespace >> takeWhile1 (`elem` alphanum)
alphanum = "_" <> ['a'..'z'] <> ['A'..'Z'] <> ['0'..'9']
token s = tr ("token " ++ show s) $ whitespace >> string s
whitespace = do
many1 $ choice [
satisfy isSpace >> skipWhile isSpace,
string "//" >> skipWhile (not . isEndOfLine) ]
return ()
genRaw = unlines $ [
"module Database.RethinkDB.Wire where",
"class WireValue a where",
" toWire :: a -> Int",
" fromWire :: Int -> Maybe a"
]
renderMessage (name, []) = Nothing
renderMessage (name, enums) = Just $ unlines $ [
unwords ["module", "Database.RethinkDB.Wire." <> name, "where"],
"import Prelude (Maybe(..), Eq, Show)",
"import Database.RethinkDB.Wire"
] ++ map renderEnum enums
renderEnum (name, decls) = unlines $ [
unwords $ ["data", name, "="] <> intersperse "|" (map fst decls),
" deriving (Eq, Show)",
unwords ["instance WireValue", name, "where"],
indent $
(for decls $ \(var, val) -> "toWire " <> var <> " = " <> val) <>
(for decls $ \(var, val) -> "fromWire " <> val <> " = Just " <> var) <>
["fromWire _ = Nothing"]
]
indent = unlines . map (" " <>)
for = flip map
tr s p = p <?> s