gdritter repos telml / 82ea3e8
ormolu Getty Ritter 1 year, 11 months ago
8 changed file(s) with 217 addition(s) and 201 deletion(s). Collapse all Expand all
11 {-# LANGUAGE LambdaCase #-}
22
3 module Data.TeLML.Parser (Fragment(..), Tag(..), Document, parse) where
3 module Data.TeLML.Parser (Fragment (..), Tag (..), Document, parse) where
44
55 import Data.Char (isAlpha, isAlphaNum, isSpace)
66 import Data.TeLML.Type
7
87 import qualified Data.Text as T
98
109 type Result a = Either String (String, a)
10
1111 type Parse a = String -> Result a
1212
1313 -- All of these characters are the ones which need escaping if used
2222 -- This is 'fmap' named in such a way that it does not conflict with
2323 -- 'fmap'.
2424 over :: (a -> b) -> Result a -> Result b
25 over _ (Left err) = Left err
25 over _ (Left err) = Left err
2626 over f (Right (s, x)) = Right (s, f x)
2727
2828 {- And this is a monadic bind. You'll note that this basically has the
3939 -}
4040 bind :: Result a -> ((String, a) -> Result b) -> Result b
4141 bind (Left err) _ = Left err
42 bind (Right a) f = f a
42 bind (Right a) f = f a
4343
4444 -- Parse a text fragment, handling escapes. This will end as soon as it
4545 -- sees any non-escaped special character.
4646 pText :: Parse Fragment
4747 pText = over (TextFrag . T.pack) . go
48 where go ('\\':x:xs)
49 | isSpecial x = (x:) `over` go xs
50 go i@(x:xs)
51 | isSpecial x = return (i, "")
52 | otherwise = (x:) `over` go xs
53 go "" = return ("", "")
48 where
49 go ('\\' : x : xs)
50 | isSpecial x = (x :) `over` go xs
51 go i@(x : xs)
52 | isSpecial x = return (i, "")
53 | otherwise = (x :) `over` go xs
54 go "" = return ("", "")
5455
5556 -- Parse a tag name of length >= 0.
5657 pTagName :: Parse String
5758 pTagName s = go s `bind` ensureName
58 where go i@(x:xs)
59 | isAlphaNum x = (x:) `over` go xs
60 | elem x "-_" = (x:) `over` go xs
61 | otherwise = return (i, "")
62 go [] = throw "unexpected end-of-document while parsing tag"
63 ensureName (xs, name)
64 | length name == 0 =
65 throw "expected tag name after `\\'"
66 | not (isAlpha (head name)) =
67 throw "tag names must begin with an alphabetic character"
68 | otherwise = return (xs, name)
59 where
60 go i@(x : xs)
61 | isAlphaNum x = (x :) `over` go xs
62 | elem x "-_" = (x :) `over` go xs
63 | otherwise = return (i, "")
64 go [] = throw "unexpected end-of-document while parsing tag"
65 ensureName (xs, name)
66 | length name == 0 =
67 throw "expected tag name after `\\'"
68 | not (isAlpha (head name)) =
69 throw "tag names must begin with an alphabetic character"
70 | otherwise = return (xs, name)
6971
7072 -- Skip any space charaters, returning () for the first non-space
7173 -- character (including EOF).
7274 skipSpace :: Parse ()
73 skipSpace i@(x:xs)
75 skipSpace i@(x : xs)
7476 | isSpace x = skipSpace xs
7577 | otherwise = return (i, ())
7678 skipSpace _ = return ("", ())
7880 -- Parse a tag assuming that a backslash has already been encountered.
7981 pTag :: Parse Fragment
8082 pTag i =
81 bind (pTagName i) $ \ (i', name) ->
83 bind (pTagName i) $ \(i', name) ->
8284 bind (skipSpace i') $ \case
83 ('{':i'', ()) -> TagFrag `over` (Tag (T.pack name) `over` pArgs i'')
84 ("",_) -> throw "unexpected end-of-document while parsing tag"
85 _ -> throw "expected start of block"
85 ('{' : i'', ()) -> TagFrag `over` (Tag (T.pack name) `over` pArgs i'')
86 ("", _) -> throw "unexpected end-of-document while parsing tag"
87 _ -> throw "expected start of block"
8688
8789 -- Parse the vertical-bar-separated arguments to a tag, ending when a
8890 -- right curly brace is encountered.
8991 pArgs :: Parse [Document]
90 pArgs ('}':xs) = return (xs, [])
92 pArgs ('}' : xs) = return (xs, [])
9193 pArgs s = bind (pFragments s) $ \case
92 ('|':xs, cs) -> (cs:) `over` pArgs xs
93 ('}':xs, cs) -> return (xs, [cs])
94 _ -> throw "[unreachable]"
94 ('|' : xs, cs) -> (cs :) `over` pArgs xs
95 ('}' : xs, cs) -> return (xs, [cs])
96 _ -> throw "[unreachable]"
9597
9698 -- Parse any fragment, deciding whether to parse it as a tag or a text chunk
9799 pFragment :: Parse Fragment
98 pFragment s@('\\':c:_)
99 | isSpecial c = pText s
100 pFragment ('\\':xs) = pTag xs
101 pFragment s = pText s
100 pFragment s@('\\' : c : _)
101 | isSpecial c = pText s
102 pFragment ('\\' : xs) = pTag xs
103 pFragment s = pText s
102104
103105 -- Parse multiple fragments, ending when it encounters a }, or |, or end-of-file.
104106 pFragments :: Parse Document
105107 pFragments "" = return ("", [])
106 pFragments ('{':s) = bind (pFragments s) $ \case
107 ('}':xs, cs) -> bind (pFragments xs) $ \(xs', cs') -> return (xs', cs ++ cs')
108 (x:_, _) -> throw ("unexpected " ++ show x ++ "; expected '}'")
109 ([], _) -> throw ("unexpected end-of-document while parsing block")
110 pFragments s@(x:_)
108 pFragments ('{' : s) = bind (pFragments s) $ \case
109 ('}' : xs, cs) -> bind (pFragments xs) $ \(xs', cs') -> return (xs', cs ++ cs')
110 (x : _, _) -> throw ("unexpected " ++ show x ++ "; expected '}'")
111 ([], _) -> throw ("unexpected end-of-document while parsing block")
112 pFragments s@(x : _)
111113 | x `elem` "}|" = return (s, [])
112 | otherwise =
113 bind (pFragment s) $ \case
114 (s', c) -> (c:) `over` pFragments s'
114 | otherwise =
115 bind (pFragment s) $ \case
116 (s', c) -> (c :) `over` pFragments s'
115117
116118 -- | Parse a string into a @TeLML@ 'Fragment'.
117119 parse :: String -> Either String Document
118120 parse str = case pFragments str of
119 Right ("", r) -> return r
120 Right ('}':_, _) -> throw ("Found unmatched '}' in document")
121 Right (s, _) -> throw ("expected end of document but found " ++ show s)
122 Left err -> throw err
121 Right ("", r) -> return r
122 Right ('}' : _, _) -> throw ("Found unmatched '}' in document")
123 Right (s, _) -> throw ("expected end of document but found " ++ show s)
124 Left err -> throw err
11 {-# LANGUAGE DeriveDataTypeable #-}
22
3 module Data.TeLML.Type (Document, Fragment(..), Tag(..)) where
3 module Data.TeLML.Type (Document, Fragment (..), Tag (..)) where
44
5 import Control.DeepSeq (NFData(..))
6 import Data.Data (Data)
5 import Control.DeepSeq (NFData (..))
6 import Data.Data (Data)
7 import Data.String (IsString (..))
78 import qualified Data.Text as T
8 import Data.Typeable (Typeable)
9 import Data.String (IsString(..))
9 import Data.Typeable (Typeable)
1010
1111 -- | A 'Document' is zero or more 'Fragment's.
1212 type Document = [Fragment]
1818 data Fragment
1919 = TextFrag T.Text
2020 | TagFrag Tag
21 deriving (Eq, Show, Typeable, Data)
21 deriving (Eq, Show, Typeable, Data)
2222
2323 data Tag = Tag
24 { tagName :: T.Text
25 , tagPayload :: [Document]
26 } deriving (Eq, Show, Typeable, Data)
24 { tagName :: T.Text,
25 tagPayload :: [Document]
26 }
27 deriving (Eq, Show, Typeable, Data)
2728
2829 instance IsString Fragment where
2930 fromString = TextFrag . fromString
3031
3132 instance NFData Fragment where
32 rnf (TextFrag s) = rnf s
33 rnf (TextFrag s) = rnf s
3334 rnf (TagFrag t) = rnf t
3435
3536 instance NFData Tag where
1 module Data.TeLML(parse, Document, Fragment(..), Tag(..)) where
1 module Data.TeLML (parse, Document, Fragment (..), Tag (..)) where
22
33 import Data.TeLML.Parser
11 import Distribution.Simple
2
23 main = defaultMain
1 {-# LANGUAGE ExistentialQuantification #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedStrings #-}
14 {-# LANGUAGE ScopedTypeVariables #-}
2 {-# LANGUAGE ExistentialQuantification #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE FlexibleInstances #-}
55 {-# LANGUAGE TypeFamilies #-}
66
77 module Data.TeLML.Markup
8 ( renderWith
9 , render
10 , basicTags
11 , mkTag
12 , simpleTag
13 , listTag
14 , H(..)
15 , Hs(..)
16 , Str(..)
17 , TagDescription
18 ) where
8 ( renderWith,
9 render,
10 basicTags,
11 mkTag,
12 simpleTag,
13 listTag,
14 H (..),
15 Hs (..),
16 Str (..),
17 TagDescription,
18 )
19 where
1920
2021 import Control.Monad (void)
2122 import Data.TeLML
2223 import qualified Data.Text as T
2324 import Text.Blaze.Html
24 import Text.Blaze.Html5 hiding (map, head, html)
25 import Text.Blaze.Html5 hiding (head, html, map)
2526 import Text.Blaze.Html5.Attributes hiding (name, span)
26
2727 import Prelude hiding (div, span)
2828
2929 -- | Render a TeLML document with an extra set of possible tags.
4040 -- splits it apart whenever it comes across double newlines.
4141 gatherPara :: Document -> [Document]
4242 gatherPara = reverse . map reverse . go [[]]
43 where go rs [] = rs
44 go (r:rs) (t@TagFrag {}:ts) = go ((t:r):rs) ts
45 go (r:rs) (TextFrag s:ts) = case splitString s of
46 [] -> go (r:rs) ts
47 [x] -> go ((TextFrag x:r):rs) ts
48 xs -> go (map ((:[]) . TextFrag) (tail xs) ++
49 ((TextFrag (head xs):r) : rs)) ts
50 go _ _ = error "[unreachable]"
43 where
44 go rs [] = rs
45 go (r : rs) (t@TagFrag {} : ts) = go ((t : r) : rs) ts
46 go (r : rs) (TextFrag s : ts) = case splitString s of
47 [] -> go (r : rs) ts
48 [x] -> go ((TextFrag x : r) : rs) ts
49 xs ->
50 go
51 ( map ((: []) . TextFrag) (tail xs)
52 ++ ((TextFrag (head xs) : r) : rs)
53 )
54 ts
55 go _ _ = error "[unreachable]"
5156
5257 -- Split a string at double-newlines.
5358 splitString :: T.Text -> [T.Text]
5459 splitString = T.splitOn "\n\n"
5560
56
5761 -- | The 'TagArguments' class allow us to define a new tag with a name
5862 -- and a simple function, and cuts out a lot of the boilerplate.
5963 class TagArguments t where
6064 toType :: t -> [T.Text]
61 taExec :: t
62 -> [Document]
63 -> (Fragment -> Either String Html)
64 -> Maybe (Either String Html)
65 taExec ::
66 t ->
67 [Document] ->
68 (Fragment -> Either String Html) ->
69 Maybe (Either String Html)
6570
6671 instance TagArguments Html where
6772 toType _ = []
6873 taExec h [] _ = Just (Right h)
69 taExec _ _ _ = Nothing
74 taExec _ _ _ = Nothing
7075
7176 instance TagArguments r => TagArguments (Str -> r) where
7277 toType _ = "str" : toType (undefined :: r)
73 taExec f ([TextFrag t]:rs) go = taExec (f (Str t)) rs go
74 taExec _ _ _ = Nothing
78 taExec f ([TextFrag t] : rs) go = taExec (f (Str t)) rs go
79 taExec _ _ _ = Nothing
7580
7681 instance TagArguments r => TagArguments (Maybe Str -> r) where
7782 toType _ = "str?" : toType (undefined :: r)
78 taExec f ([TextFrag t]:rs) go = taExec (f (Just (Str t))) rs go
79 taExec f [] go = taExec (f Nothing) [] go
80 taExec _ _ _ = Nothing
83 taExec f ([TextFrag t] : rs) go = taExec (f (Just (Str t))) rs go
84 taExec f [] go = taExec (f Nothing) [] go
85 taExec _ _ _ = Nothing
8186
8287 instance TagArguments r => TagArguments (H -> r) where
8388 toType _ = "frag" : toType (undefined :: r)
84 taExec f (doc:rs) go =
89 taExec f (doc : rs) go =
8590 let h = fmap sequence_ (mapM go doc)
86 in case h of
87 Left err -> return (Left err)
88 Right h' -> taExec (f (H h')) rs go
89 taExec _ [] _ = Nothing
91 in case h of
92 Left err -> return (Left err)
93 Right h' -> taExec (f (H h')) rs go
94 taExec _ [] _ = Nothing
9095
9196 instance (h ~ Html) => TagArguments (Hs -> h) where
9297 toType _ = ["..."]
9398 taExec f docs go =
9499 let h = mapM (fmap sequence_ . mapM go) docs
95 in case h of
96 Left err -> return (Left err)
97 Right hs -> return (Right (f (Hs hs)))
100 in case h of
101 Left err -> return (Left err)
102 Right hs -> return (Right (f (Hs hs)))
98103
99104 data TagDescription
100 = forall t. TagArguments t =>
105 = forall t.
106 TagArguments t =>
101107 TagDescription T.Text t
102108
103109 -- | The 'Str' newtype will match a literal chunk of non-formatted,
104110 -- non-structured text.
105 newtype Str = Str { fromStr :: T.Text }
111 newtype Str = Str {fromStr :: T.Text}
106112
107113 -- | The 'H' newtype will match a single, pre-rendered argument
108 newtype H = H { fromHtml :: Html }
114 newtype H = H {fromHtml :: Html}
109115
110116 -- | The 'Hs' newtype will match a concatenated set of pre-rendered
111117 -- arguments
112 newtype Hs = Hs { fromHtmlList :: [Html] }
118 newtype Hs = Hs {fromHtmlList :: [Html]}
113119
114120 mkTag :: TagArguments t => T.Text -> t -> TagDescription
115121 mkTag = TagDescription
117123 -- The built-in set of tags (subject to change)
118124 basicTags :: [TagDescription]
119125 basicTags =
120 [ simpleTag "em" em
121 , simpleTag "strong" strong
122 , simpleTag "li" li
123 , simpleTag "h1" h1
124 , simpleTag "h2" h2
125 , simpleTag "p" (\ rs -> span ! class_ "para" $ rs)
126 , simpleTag "blockquote" blockquote
127 , simpleTag "tt" code
128 , simpleTag "code" (pre . code)
129 , simpleTag "ttcom" (\ rs -> span ! class_ "comment" $ rs)
130 , simpleTag "ttkw" (\ rs -> span ! class_ "keyword" $ rs)
131 , simpleTag "ttcn" (\ rs -> span ! class_ "constr" $ rs)
132 , simpleTag "ttstr" (\ rs -> span ! class_ "string" $ rs)
133
134 , listTag "ul" ul
135 , listTag "ol" ol
136 , mkTag "list" (\ (Hs hs) -> ul $ mapM_ li hs)
137 , listTag "center" (\ rs -> div ! class_ "center" $ rs)
138
139 , TagDescription "br" br
140 , TagDescription "comment" ("" :: Html)
141 , TagDescription "link" (\ (Str l) (H h) -> a ! href (toValue l) $ h)
142 , TagDescription "img" $ \ (Str l) altText -> case altText of
143 Just r -> img ! src (toValue l) ! alt (toValue (fromStr r))
126 [ simpleTag "em" em,
127 simpleTag "strong" strong,
128 simpleTag "li" li,
129 simpleTag "h1" h1,
130 simpleTag "h2" h2,
131 simpleTag "p" (\rs -> span ! class_ "para" $ rs),
132 simpleTag "blockquote" blockquote,
133 simpleTag "tt" code,
134 simpleTag "code" (pre . code),
135 simpleTag "ttcom" (\rs -> span ! class_ "comment" $ rs),
136 simpleTag "ttkw" (\rs -> span ! class_ "keyword" $ rs),
137 simpleTag "ttcn" (\rs -> span ! class_ "constr" $ rs),
138 simpleTag "ttstr" (\rs -> span ! class_ "string" $ rs),
139 listTag "ul" ul,
140 listTag "ol" ol,
141 mkTag "list" (\(Hs hs) -> ul $ mapM_ li hs),
142 listTag "center" (\rs -> div ! class_ "center" $ rs),
143 TagDescription "br" br,
144 TagDescription "comment" ("" :: Html),
145 TagDescription "link" (\(Str l) (H h) -> a ! href (toValue l) $ h),
146 TagDescription "img" $ \(Str l) altText -> case altText of
147 Just r -> img ! src (toValue l) ! alt (toValue (fromStr r))
144148 Nothing -> img ! src (toValue l)
145149 ]
146150
156160 -- render a single paragraph
157161 renderPara :: [TagDescription] -> Document -> Either String Html
158162 renderPara taglist ds = fmap (p . sequence_) (mapM go ds)
159 where go (TextFrag ts) = Right (toMarkup ts)
160 go (TagFrag (Tag tx rs)) = exec tx rs taglist
161 exec name args (TagDescription tag func:_)
162 | name == tag = case taExec func args go of
163 Nothing -> Left $ unwords
164 [ "Tag"
165 , T.unpack ('\\' `T.cons` name)
166 , "expects argument structure"
167 , T.unpack ('\\' `T.cons` name `T.append` argsFor func)
168 ]
169 Just x -> x
170 exec name args (_:rs) = exec name args rs
171 exec name args [] = Left $
172 "Error: no match for tag " ++ T.unpack name ++ "/" ++ show (length args)
163 where
164 go (TextFrag ts) = Right (toMarkup ts)
165 go (TagFrag (Tag tx rs)) = exec tx rs taglist
166 exec name args (TagDescription tag func : _)
167 | name == tag = case taExec func args go of
168 Nothing ->
169 Left $
170 unwords
171 [ "Tag",
172 T.unpack ('\\' `T.cons` name),
173 "expects argument structure",
174 T.unpack ('\\' `T.cons` name `T.append` argsFor func)
175 ]
176 Just x -> x
177 exec name args (_ : rs) = exec name args rs
178 exec name args [] =
179 Left $
180 "Error: no match for tag " ++ T.unpack name ++ "/" ++ show (length args)
11 module Main where
22
3 import Control.Monad ((>=>))
3 import Control.Monad ((>=>))
44 import qualified Data.TeLML as TeLML
55 import qualified Data.TeLML.Markup as TeLML
6 import qualified System.Console.GetOpt as Opt
7 import qualified System.Environment as Env
8 import qualified System.Exit as Sys
69 import qualified Text.Blaze.Renderer.String as B
7 import qualified System.Console.GetOpt as Opt
8 import qualified System.Exit as Sys
9 import qualified System.Environment as Env
1010
1111 data Options = Options
12 { optInput :: Maybe FilePath
13 , optOutput :: Maybe FilePath
12 { optInput :: Maybe FilePath,
13 optOutput :: Maybe FilePath
1414 }
1515
1616 defaultOptions :: Options
17 defaultOptions = Options
18 { optInput = Nothing
19 , optOutput = Nothing
20 }
17 defaultOptions =
18 Options
19 { optInput = Nothing,
20 optOutput = Nothing
21 }
2122
2223 options :: [Opt.OptDescr (Options -> Options)]
2324 options =
24 [ Opt.Option ['i'] ["input"]
25 (Opt.ReqArg (\ path opt -> opt { optInput = Just path }) "file")
26 "Read input from this file"
27 , Opt.Option ['o'] ["output"]
28 (Opt.ReqArg (\ path opt -> opt { optOutput = Just path }) "file")
29 "Read input from this file"
25 [ Opt.Option
26 ['i']
27 ["input"]
28 (Opt.ReqArg (\path opt -> opt {optInput = Just path}) "file")
29 "Read input from this file",
30 Opt.Option
31 ['o']
32 ["output"]
33 (Opt.ReqArg (\path opt -> opt {optOutput = Just path}) "file")
34 "Read input from this file"
3035 ]
3136
3237 doRender :: String -> Either String String
3641 runPipeline inF outF = do
3742 cs <- case inF of
3843 Nothing -> getContents
39 Just f -> readFile f
44 Just f -> readFile f
4045 case doRender cs of
4146 Left err -> Sys.die err
4247 Right rs -> case outF of
4348 Nothing -> putStrLn rs
44 Just f -> writeFile f rs
49 Just f -> writeFile f rs
4550
4651 main :: IO ()
4752 main = do
4954 case opts of
5055 (fs, [], []) -> do
5156 let Options
52 { optInput = inF
53 , optOutput = outF
57 { optInput = inF,
58 optOutput = outF
5459 } = foldr id defaultOptions fs
5560 runPipeline inF outF
5661 (_, _, _) -> do
55 import Control.Monad ((>=>))
66 import Data.TeLML
77 import Data.TeLML.Markup
8 import Test.Hspec
89 import Text.Blaze.Renderer.String (renderMarkup)
9
10 import Test.Hspec
1110
1211 main :: IO ()
1312 main = hspec spec
2322 it "should embolden" $ do
2423 doRender "\\strong{foo}" `shouldBe` Right "<p><strong>foo</strong></p>"
2524 it "should list" $ do
26 doRender "\\ul{\\li{one}\\li{two}}" `shouldBe`
27 Right "<p><ul><li>one</li><li>two</li></ul></p>"
25 doRender "\\ul{\\li{one}\\li{two}}"
26 `shouldBe` Right "<p><ul><li>one</li><li>two</li></ul></p>"
11 {-# LANGUAGE RecordWildCards #-}
22
33 module Data.TeLML.Parse
4 ( Fragment(..)
5 , Document
6 , Parse
7 , decode
8 , parse
9 , select
10 , field
11 , text
12 , document
13 , arg
14 , both
15 ) where
4 ( Fragment (..),
5 Document,
6 Parse,
7 decode,
8 parse,
9 select,
10 field,
11 text,
12 document,
13 arg,
14 both,
15 )
16 where
1617
18 import Data.TeLML
1719 import qualified Data.Text as T
1820
19 import Data.TeLML
20
21 newtype Parse t a = Parse { runParse :: t -> Either String a }
21 newtype Parse t a = Parse {runParse :: t -> Either String a}
2222
2323 decode :: String -> Parse Document r -> Either String r
2424 decode str content = case parse str of
2525 Left err -> Left err
26 Right x -> runParse content x
26 Right x -> runParse content x
2727
2828 instance Functor (Parse t) where
29 fmap f (Parse g) = Parse (\ x -> fmap f (g x))
29 fmap f (Parse g) = Parse (\x -> fmap f (g x))
3030
3131 instance Applicative (Parse t) where
32 pure x = Parse (\ _ -> Right x)
32 pure x = Parse (\_ -> Right x)
3333 f <*> x =
34 f >>= \ f' ->
35 x >>= \ x' ->
36 pure (f' x')
34 f >>= \f' ->
35 x >>= \x' ->
36 pure (f' x')
3737
3838 instance Monad (Parse t) where
39 Parse x >>= f = Parse $ \ s ->
39 Parse x >>= f = Parse $ \s ->
4040 case x s of
4141 Left err -> Left err
4242 Right v -> runParse (f v) s
4343
4444 select :: T.Text -> Parse [Document] t -> Parse Document [t]
45 select name content = Parse $ \ s -> each s
45 select name content = Parse $ \s -> each s
4646 where
4747 each [] = return []
48 each (TagFrag (Tag t doc):xs)
48 each (TagFrag (Tag t doc) : xs)
4949 | t == name = (:) <$> runParse content doc <*> each xs
50 each (_:xs) = each xs
50 each (_ : xs) = each xs
5151
5252 field :: T.Text -> (Parse [Document] t) -> Parse Document t
53 field name content = Parse $ \ s -> find s
53 field name content = Parse $ \s -> find s
5454 where
5555 find [] = Left ("Unable to find tag \\" ++ T.unpack name)
56 find (TagFrag (Tag t doc):_)
56 find (TagFrag (Tag t doc) : _)
5757 | t == name = runParse content doc
58 find (_:xs) = find xs
58 find (_ : xs) = find xs
5959
6060 arg :: Parse Document t -> Parse [Document] t
61 arg f = Parse $ \ s ->
61 arg f = Parse $ \s ->
6262 case s of
6363 [x] -> runParse f x
6464 _ -> Left ("Wrong arity for `arg`: " ++ show (length s))
6565
66
6766 both :: Parse Document a -> Parse Document b -> Parse [Document] (a, b)
68 both l r = Parse $ \ s ->
67 both l r = Parse $ \s ->
6968 case s of
7069 [a, b] -> (,) <$> runParse l a <*> runParse r b
7170 _ -> Left ("Wrong arity for `both`: " ++ show (length s))
7271
7372 text :: Parse Document T.Text
74 text = Parse (\ s -> T.concat <$> traverse go s)
75 where go (TextFrag str) = Right str
76 go (TagFrag (Tag t _)) = Left ("Expected Text fragment, found \\" ++ T.unpack t)
73 text = Parse (\s -> T.concat <$> traverse go s)
74 where
75 go (TextFrag str) = Right str
76 go (TagFrag (Tag t _)) = Left ("Expected Text fragment, found \\" ++ T.unpack t)
7777
7878 document :: Parse Document Document
79 document = Parse (\ s -> Right s)
79 document = Parse (\s -> Right s)