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
|
{- Copyright © 2015 Benjamin Barenblat
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>. -}
{-# LANGUAGE RecordWildCards #-}
module Fragment ( Fragment
, CodeOrReference(..)
, isBlockCode
, blockName, blockContents
, parseStdin
, parseFile) where
import Data.Data (Data)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import Control.Applicative ((<$>), (<*>))
import Control.Monad (void)
import System.IO (hGetContents, stdin)
import Text.Parsec
import Text.Parsec.String
data Fragment = Documentation String
| BlockCode String [CodeOrReference]
deriving (Eq, Show, Data, Typeable, Generic)
isBlockCode :: Fragment -> Bool
isBlockCode (Documentation {..}) = False
isBlockCode (BlockCode {..}) = True
blockName :: Fragment -> String
blockName (Documentation {..}) = error "Documentation fragments are unnamed"
blockName (BlockCode name _) = name
blockContents :: Fragment -> [CodeOrReference]
blockContents (Documentation {..}) = error "Documentation fragments have no code"
blockContents (BlockCode _ body) = body
data CodeOrReference = Code String
| Reference String
deriving (Eq, Show, Data, Typeable, Generic)
parseStdin :: IO (Either ParseError [Fragment])
parseStdin = parse literateFile "<stdin>" <$> hGetContents stdin
parseFile :: FilePath -> IO (Either ParseError [Fragment])
parseFile = parseFromFile literateFile
literateFile :: Parser [Fragment]
literateFile = many (blockCode <|> documentation)
documentation :: Parser Fragment
documentation = do
body <- many1Till anyChar (eof <|> lookAhead (void blockCode))
return $ Documentation body
blockCode :: Parser Fragment
blockCode = do
void $ string "<<"
name <- many1Till anyChar (try (string ">>=" <?> "start of code block"))
body <- many1Till (reference <|> code) (char '@')
return $ BlockCode name body
code :: Parser CodeOrReference
code = do
body <- many1Till anyChar (lookAhead $ (void (char '@'))
<|> (void reference))
return $ Code body
reference :: Parser CodeOrReference
reference = do
void $ string "<<"
name <- many1Till anyChar (try $ string ">>")
return $ Reference name
many1Till :: Stream s m t
=> ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
many1Till p end = (:) <$> p <*> manyTill p end
|