-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbipandoc.hs
143 lines (109 loc) · 5.88 KB
/
bipandoc.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import qualified Generics.BiGUL
import qualified Generics.BiGUL.Interpreter as BiGUL
import qualified Generics.BiGUL.TH
import Generics.BiGUL.Error (BiGULTrace)
import Abstract
import qualified BX.HTMLBX as HTMLBX
import qualified BX.MarkdownBX as MarkdownBX
import qualified CST.HTMLParser as HTMLParser
import qualified CST.Markdown as MarkdownParser
import Data.Maybe
import System.Environment
import System.IO
import System.Exit
import Text.Show.Pretty
import qualified Options.Applicative as OA
import Options.Applicative ((<>), (<$>), (<*>))
import Debug.Trace
data Options = Options { srcFormat :: String, dstFormat :: String, dstFile :: String, outputFile :: String, checkAmbiguity :: Bool, srcFile :: String } deriving (Show)
withDefaultValue a = fmap (fromMaybe a)
optsParser = Options <$> OA.strOption ( OA.long "from" <> OA.short 'f' <> OA.metavar "FORMAT" <> OA.help "Source format")
<*> OA.strOption ( OA.long "to" <> OA.short 't' <> OA.metavar "FORMAT" <> OA.help "Destination format")
<*> withDefaultValue "" (OA.optional (OA.strOption ( OA.long "dst" <> OA.short 'd' <> OA.metavar "FILENAME" <> OA.help "Destination filename. Use an empty document if not specified.")))
<*> withDefaultValue "" (OA.optional (OA.strOption ( OA.long "output" <> OA.short 'o' <> OA.metavar "FILENAME" <> OA.help "Output filename")))
<*> OA.switch (OA.long "check-ambiguity" <> OA.help "Check the format ambiguity when printing")
<*> withDefaultValue "" (OA.optional $ OA.argument OA.str (OA.metavar "FILE"))
optsWithInfo = OA.info (OA.helper <*> optsParser) (OA.fullDesc <> OA.progDesc "Supported formats: markdown, html\n" <> OA.header "bipandoc - a bidirectional document converter")
get :: Options -> String -> Maybe AbsDocument
get opt src = case srcFormat opt of
"html" -> BiGUL.get HTMLBX.htmlBX (HTMLParser.parseHTML src)
"html-body" -> BiGUL.get HTMLBX.htmlBX (HTMLParser.parseHTMLBody src)
"markdown" -> BiGUL.get MarkdownBX.markdownBX (MarkdownParser.parseMarkdown (addNewline src))
f -> error ("Invalid source format: " ++ f)
where addNewline s = if null s || last s /= '\n' then s ++ "\n" else s
getTrace :: Options -> String -> BiGULTrace
getTrace opt src = case srcFormat opt of
"html" -> BiGUL.getTrace HTMLBX.htmlBX (HTMLParser.parseHTML src)
"html-body" -> BiGUL.getTrace HTMLBX.htmlBX (HTMLParser.parseHTMLBody src)
"markdown" -> BiGUL.getTrace MarkdownBX.markdownBX (MarkdownParser.parseMarkdown (addNewline src))
f -> error ("Invalid source format: " ++ f)
where addNewline s = if null s || last s /= '\n' then s ++ "\n" else s
put :: Options -> String -> AbsDocument -> Maybe String
put opt src view = case dstFormat opt of
"html" -> put' (HTMLBX.htmlBX, HTMLParser.parseHTML, HTMLParser.prtDocument)
"html-body" -> put' (HTMLBX.htmlBX, HTMLParser.parseHTMLBody, HTMLParser.prtDocumentBody)
"markdown" -> put' (MarkdownBX.markdownBX, MarkdownParser.parseMarkdown, MarkdownParser.printMarkdown)
f -> error ("Invalid target format: " ++ f)
where put' (bx, parser, printer) = do
src' <- BiGUL.put bx (parser src) view
let result = printer src'
if not (checkAmbiguity opt)
then return result
else if BiGUL.get bx (parser result) /= Just view
then do
traceM "Format ambiguity detected! get(parser(result)) /= original_ast"
traceM $ "the original view is:\n" ++ (ppShow (Just view))
traceM "vs"
traceM $ "get(parser(output)) is:\n" ++ (ppShow $ BiGUL.get bx (parser result))
return result
else return result
putTrace :: Options -> String -> AbsDocument -> BiGULTrace
putTrace opt src view = case dstFormat opt of
"html" -> put' (HTMLBX.htmlBX, HTMLParser.parseHTML, HTMLParser.prtDocument)
"html-body" -> put' (HTMLBX.htmlBX, HTMLParser.parseHTMLBody, HTMLParser.prtDocumentBody)
"markdown" -> put' (MarkdownBX.markdownBX, MarkdownParser.parseMarkdown, MarkdownParser.printMarkdown)
f -> error ("Invalid target format: " ++ f)
where put' (bx, parser, printer) = BiGUL.putTrace bx (parser src) view
defaultDocument :: String -> String
defaultDocument format =
case format of
"html" -> HTMLParser.emptyHTMLStr
"html-body" -> HTMLParser.emptyHTMLBodyStr
"markdown" -> MarkdownParser.defaultMarkdown
_ -> ""
main = do
opts <- OA.execParser optsWithInfo
-- Read source
srcH <- if srcFile opts == ""
then return stdin
else openFile (srcFile opts) ReadMode
src <- hGetContents srcH
let viewM = get opts src
-- MarkdownParser.putPretty viewM
if isNothing viewM
then do
putStrLn $ "Failed to get view from " ++ src
print $ getTrace opts src
exitFailure
else do
let (Just view) = viewM
-- Read sychronization target, use a default document if not specied
dst <- if dstFile opts == ""
then return $ defaultDocument (dstFormat opts)
else do
dstH <- openFile (dstFile opts) ReadMode
hGetContents dstH
let targetM = put opts dst view
if isNothing targetM
then do
putStrLn "Failed to put-back into target, see trace:"
print $ putTrace opts dst view
exitFailure
else do
let (Just target) = targetM
outH <- if outputFile opts == ""
then return stdout
else openFile (outputFile opts) WriteMode
hPutStr outH target
hClose outH
exitSuccess