summaryrefslogtreecommitdiff
path: root/src/Fragment.hs
blob: 6850662932635b636878a4c7c306b2ba4cf48803 (plain)
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
{- 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/>. -}

module Fragment ( Fragment(..)
                , CodeOrReference(..)
                , 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)

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