diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-18 05:42:26 +0000 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-18 05:42:26 +0000 |
commit | 4914e4d7f638b7771ecdaf24ee0516c05505697c (patch) | |
tree | 6c89d30ae2ea97f14f4a35af9013dc74e747492c /bbcode/src | |
parent | 539b69b3e3b0c4ffee2323a8fe6239ebda4af48c (diff) | |
download | thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar.gz thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar.bz2 thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar.xz thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.zip |
Disallow paragraphs everywhere but at the top
Diffstat (limited to 'bbcode/src')
-rw-r--r-- | bbcode/src/Text/BBCode.hs | 37 |
1 files changed, 21 insertions, 16 deletions
diff --git a/bbcode/src/Text/BBCode.hs b/bbcode/src/Text/BBCode.hs index fbd8a33..8f04f19 100644 --- a/bbcode/src/Text/BBCode.hs +++ b/bbcode/src/Text/BBCode.hs | |||
@@ -6,6 +6,7 @@ module Text.BBCode | |||
6 | ( bbcode | 6 | ( bbcode |
7 | , BBCodeError(..) | 7 | , BBCodeError(..) |
8 | , TreeError(..) | 8 | , TreeError(..) |
9 | , DomForest | ||
9 | , DomTree(..) | 10 | , DomTree(..) |
10 | -- , dom | 11 | -- , dom |
11 | -- , BBLabel | 12 | -- , BBLabel |
@@ -40,19 +41,23 @@ import Data.Bifunctor (Bifunctor(first)) | |||
40 | 41 | ||
41 | -- | Our target structure -- a rose tree with an explicit terminal constructor | 42 | -- | Our target structure -- a rose tree with an explicit terminal constructor |
42 | data DomTree = Element (CI Text) (Map (CI Text) Text) [DomTree] | 43 | data DomTree = Element (CI Text) (Map (CI Text) Text) [DomTree] |
43 | | Paragraph [DomTree] | ||
44 | | Content Text | 44 | | Content Text |
45 | deriving (Show, Eq) | 45 | deriving (Show, Eq) |
46 | 46 | ||
47 | dom :: Forest BBLabel -> [DomTree] | 47 | -- | List of paragraphs, which are comprised of lists of 'DomTree's |
48 | type DomForest = [[DomTree]] | ||
49 | |||
50 | dom :: Forest BBLabel -> DomForest | ||
48 | -- ^ Parse semantically constrained rose tree to syntactically constrained version | 51 | -- ^ Parse semantically constrained rose tree to syntactically constrained version |
49 | -- | 52 | -- |
50 | -- Silently drops children of semantically terminal nodes ('BBPlain') | 53 | -- Silently drops children of semantically terminal nodes ('BBPlain') and paragraphs anywhere but at top level |
51 | dom = map dom' | 54 | dom = map fromPar |
52 | where | 55 | where |
53 | dom' (Node (BBPlain t) _) = Content t | 56 | fromPar (Node BBPar ts) = concatMap dom' ts |
54 | dom' (Node (BBTag t attrs) ts) = Element (CI.mk t) (Map.mapKeys CI.mk attrs) $ map dom' ts | 57 | fromPar x = dom' x |
55 | dom' (Node BBPar ts) = Paragraph $ map dom' ts | 58 | dom' (Node (BBPlain t) _) = pure $ Content t |
59 | dom' (Node (BBTag t attrs) ts) = pure . Element (CI.mk t) (Map.mapKeys CI.mk attrs) $ concatMap dom' ts | ||
60 | dom' (Node BBPar _) = [] | ||
56 | 61 | ||
57 | -- | Errors encountered during parsing | 62 | -- | Errors encountered during parsing |
58 | data BBCodeError = LexerError String -- ^ Error while parsing input to stream of tokens | 63 | data BBCodeError = LexerError String -- ^ Error while parsing input to stream of tokens |
@@ -61,13 +66,14 @@ data BBCodeError = LexerError String -- ^ Error while parsing input to stream of | |||
61 | 66 | ||
62 | instance Exception BBCodeError | 67 | instance Exception BBCodeError |
63 | 68 | ||
64 | bbcode :: Text -> Either BBCodeError [DomTree] | 69 | bbcode :: Text -> Either BBCodeError DomForest |
65 | -- ^ Parse BBCode | 70 | -- ^ Parse BBCode |
66 | bbcode t = fmap dom $ first LexerError (parseOnly (many token <* endOfInput) t) >>= first TreeError . rose | 71 | bbcode t = fmap dom $ first LexerError (parseOnly (many token <* endOfInput) t) >>= first TreeError . rose |
67 | 72 | ||
68 | -- | Errors in input encountered during parsing of lexed token-stream | 73 | -- | Errors in input encountered during parsing of lexed token-stream |
69 | data TreeError = MismatchedTags Text Text -- ^ Closing tags label does not match opening tags | 74 | data TreeError = MismatchedTags Text Text -- ^ Closing tags label does not match opening tags |
70 | | ImbalancedTags Text -- ^ We found an extraneous closing tag | 75 | | ImbalancedTags Text -- ^ We found an extraneous closing tag |
76 | | ParagraphWithinTag -- ^ We found a paragraph-break within a tag | ||
71 | deriving (Show, Eq, Generic, Typeable) | 77 | deriving (Show, Eq, Generic, Typeable) |
72 | 78 | ||
73 | instance Exception TreeError | 79 | instance Exception TreeError |
@@ -93,7 +99,7 @@ rose :: [BBToken] -> Either TreeError (Forest BBLabel) | |||
93 | rose = fmap Z.toForest . foldM (flip rose') (Z.fromForest []) | 99 | rose = fmap Z.toForest . foldM (flip rose') (Z.fromForest []) |
94 | where | 100 | where |
95 | rose' (BBStr t) = return . Z.nextSpace . Z.insert (Node (BBPlain t) []) | 101 | rose' (BBStr t) = return . Z.nextSpace . Z.insert (Node (BBPlain t) []) |
96 | rose' BBNewPar = return . parBreak -- for more pointless | 102 | rose' BBNewPar = parBreak -- for more pointless |
97 | rose' (BBOpen t attrs) = return . Z.children . Z.insert (Node (BBTag t $ Map.fromList attrs) []) | 103 | rose' (BBOpen t attrs) = return . Z.children . Z.insert (Node (BBTag t $ Map.fromList attrs) []) |
98 | rose' (BBContained t attrs) = return . Z.nextSpace . Z.insert (Node (BBTag t $ Map.fromList attrs) []) | 104 | rose' (BBContained t attrs) = return . Z.nextSpace . Z.insert (Node (BBTag t $ Map.fromList attrs) []) |
99 | rose' (BBClose t) = close t -- for more pointless | 105 | rose' (BBClose t) = close t -- for more pointless |
@@ -110,15 +116,14 @@ rose = fmap Z.toForest . foldM (flip rose') (Z.fromForest []) | |||
110 | | isPar . Z.tree $ pos = Z.parent pos >>= traversePars | 116 | | isPar . Z.tree $ pos = Z.parent pos >>= traversePars |
111 | | otherwise = return pos | 117 | | otherwise = return pos |
112 | 118 | ||
113 | parBreak :: TreePos Empty BBLabel -> TreePos Empty BBLabel | 119 | parBreak :: TreePos Empty BBLabel -> Either TreeError (TreePos Empty BBLabel) |
114 | parBreak z = let siblings = reverse $ Z.before z -- We only move ever move right so Z.after will always be empty | 120 | parBreak z = let siblings = reverse $ Z.before z |
115 | 121 | ||
116 | siblingsAsPars | 122 | siblingsAsPars |
117 | | all isPar siblings = z | 123 | | all isPar siblings = Right z |
118 | | otherwise = case Z.parent z of | 124 | | Z.isRoot z = Right . Z.fromForest $ [Node BBPar siblings] |
119 | Nothing -> Z.fromForest $ [Node BBPar siblings] | 125 | | otherwise = Left ParagraphWithinTag |
120 | Just p -> Z.children . Z.modifyTree (\(Node l _) -> Node l [Node BBPar siblings]) $ p | 126 | in Z.children . Z.insert (Node BBPar []) . Z.last <$> siblingsAsPars |
121 | in Z.children . Z.insert (Node BBPar []) . Z.last $ siblingsAsPars | ||
122 | 127 | ||
123 | isPar (Node BBPar _) = True | 128 | isPar (Node BBPar _) = True |
124 | isPar _ = False | 129 | isPar _ = False |